如何转换Option [List [String]]中的List [Any]

时间:2014-09-28 19:28:28

标签: scala collections type-safety

给定List [Any],我想将其转换为Option [List [String]]

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

有更好的方法吗?

6 个答案:

答案 0 :(得分: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

或解决用例:

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))

答案 1 :(得分:2)

已经有不少答案,但我认为它们比所需要的更聪明。问题中的初始提案并不是那么糟糕,除非我会将getClass测试替换为isInstanceOf

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

它的功能,只复制一次列表。是的,列表遍历了两次,但通常它仍然比抛出异常更快(这通常很慢 - 如果你真的想要走这条路线,至少使用ControlThrowable,这不是在构造时记录堆栈跟踪)。

此外,由于@ som-snytt在评论中静静地指出,由于删除,你甚至不需要在列表中强制转换所有元素。您也可以投射列表,在检查所有元素为String之后,列表与其他任何投射一样安全:

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

这是最有效的版本,因为根本没有列表复制。

答案 2 :(得分:0)

有一个toString方法,可以从任何对象创建一个String。因此,如果不要求原始List的所有元素实际上都是String元素,那么您可以这样做:

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

尝试将返回Some(x)如果成功并获得值x,否则返回None。

如果转换只有在所有元素都是字符串时才能成功,那么我们可以在Try内部进行转换(在第一次失败时,Try会失败,因此我们将获得None)

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

答案 3 :(得分:0)

我建议使用模式匹配:

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

答案 4 :(得分: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> 

答案 5 :(得分:0)

使用scalaz有一个简单的解决方案:

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