2014-09-28 30 views
0

給定一個列表[任何],我想將其轉換爲一個選項[列表[字符串]如何列表[任何]中選擇[列表[字符串]

def convert(ls: List[Any]) : Option[List[String]] = { 
    if (ls.forall(_.getClass == classOf[String])) 
    Some(ls.map(_.asInstanceOf[String])) 
    else 
    None 
} 

是否有轉換更好的方法 ?

+1

我忘了,如果它是有效的Java或者喬希布洛赫要求,因爲它看起來像一個我們不使用ELL的瓦爾的謎題。在我升級眼鏡之前,我可能會開展一個推特活動。 :) – 2014-09-28 19:59:55

+0

你是對的,我剛剛修好了 – 2014-09-28 20:16:22

+0

其實,我認爲他的情況是很長的後綴,'1l',對此很抱歉;但我有相同的視覺解析問題。謝謝。 – 2014-09-28 20:19:29

回答

0

有一個toString方法,它可以從任何對象創建一個字符串。所以,如果它不是一個要求,即原來的列表中的所有元素實際上應該是字符串元素,你可以這樣做:

import scala.util.Try 
def convert(l: List[Any]) : Option[List[String]] = { 
    Try(l.map(_.toString)).toOption 
} 

嘗試將返回一些(x)如果它成功,並獲得x的值,或無其它方式。

如果轉換成功,應該只有當所有的元素都是字符串,那麼我們可以做內部試轉換(在第一次失敗,嘗試將失敗,因此我們會得到無)

import scala.util.Try 
def convert(l: List[Any]) : Option[List[String]] = { 
    Try(l.map(_.asInstanceOf[String])).toOption 
} 
+0

正如在我的例子中,我想確保所有的元素都是字符串 – 2014-09-28 19:49:03

+0

然後_.toString應該替換爲_.asInstanceOf [String]。 – Ashalynd 2014-09-28 19:57:09

+0

哼哼......我更喜歡在之前檢查類型,而不是拋出異常(至少出於性能原因)。 – 2014-09-28 20:08:07

0

我會推薦使用的模式匹配:

def convert(l: List[Any]) : Option[List[String]] = { 
Try(list.collect{ 
    case s : String => s 
    case x : Any => throw new Exception() 
    }).toOption 
} 
+0

正如在我的例子中,我想確保所有元素都是字符串 – 2014-09-28 19:50:29

+0

我更新了答案,結合@Ashalynd解決方案與我的實現該 – 2014-09-28 19:59:22

+0

嗡嗡聲...我更喜歡檢查類型而不是拋出異常。 – 2014-09-28 20:04:32

3

喜歡:

scala> val bag = List("a", 1, 2.0, "b") 
bag: List[Any] = List(a, 1, 2.0, b) 

scala> def unbagged(vs: List[Any]): Option[List[String]] = Option(vs collect { case s: String => s}) filter (_.nonEmpty) 
unbagged: (vs: List[Any])Option[List[String]] 

scala> unbagged(bag) 
res0: Option[List[String]] = Some(List(a, b)) 

scala> unbagged(List(1, 3.14)) 
res1: Option[List[String]] = None 

,或者尋址第e使用案例:

scala> def strung(vs: List[Any]): Option[List[String]] = (Option(vs) filter (_ forall { case _: String => true case _ => false })).asInstanceOf[Option[List[String]]] 
strung: (vs: List[Any])Option[List[String]] 

scala> strung(bag) 
res3: Option[List[String]] = None 

scala> strung(List("a","b","c")) 
res4: Option[List[String]] = Some(List(a, b, c)) 
+0

正如我的例子,我想確保所有元素都是字符串 – 2014-09-28 20:04:52

+0

是的,我想我們錯過了那部分。 – 2014-09-28 20:24:02

+0

imho,'_.isInstanceOf [String]'比'{case _:String => true case _ => false'更可讀, – 2015-06-22 20:19:17

0

代碼有點醜,但它的工作原理。 它不使用classOf但它使用模式匹配:

scala> val l1 = List("a", 1, 12.0) 
l1: List[Any] = List(a, 1, 12.0) 

scala> val l2 = List[Any]("a", "b", "c") 
l2: List[Any] = List(a, b, c) 

scala> def convert(list: List[Any]) = { 
    |  list.foldLeft(Some(List()): Option[List[String]]) { (x, y) => 
    |   x match { 
    |   case Some(l) => 
    |    y match { 
    |    case elem: String => Some(l ::: List(elem)) 
    |    case _ => None 
    |    } 
    |   case None => None 
    |   } 
    |  } 
    |  } 
convert: (list: List[Any])Option[List[String]] 

scala> convert(l1) 
res12: Option[List[String]] = None 

scala> convert(l2) 
res13: Option[List[String]] = Some(List(a, b, c)) 

scala> 
2

已經有相當多的答案,但我認爲他們都是比需要聰明。在這個問題的初步建議並不壞,但我會通過isInstanceOf更換getClass測試:

def convert(ls: List[Any]): Option[List[String]] = { 
    if (ls.forall(_.isInstanceOf[String])) 
    Some(ls.map(_.asInstanceOf[String])) 
    else 
    None 
} 

它的功能,副本列表中只有一次。是的,列表遍歷了兩次,但通常它會比拋出異常更快(通常很慢 - 如果您真的想要走這條路,至少使用ControlThrowable,它不記錄堆棧跟蹤當被構造時)。

此外,由於@ som-snytt在評論中悄悄指出,由於刪除,您甚至不需要將所有元素都放在列表中。你不妨也投了名單,其中,在已經檢查了所有的元素都是String S,只是像任何其他類型轉換爲安全:

def convert(ls: List[Any]): Option[List[String]] = { 
    if (ls.forall(_.isInstanceOf[String])) 
    Some(ls.asInstanceOf[List[String]]) 
    else 
    None 
} 

這是目前最有效的版本,因爲沒有列表根本不需要複製。

0

有使用scalaz一個簡單的解決方案:

def convert(ls: List[Any]) : Option[List[String]] = 
    ls.map { a => if (a.isInstanceOf[String]) Some(a.asInstanceOf[String]) else None}.sequence 
相關問題