我有一个混合值列表:
val list = List("A", 2, 'c', 4)
我知道如何在一次操作中收集字符,字符串或整数:
val strings = list collect { case s:String => s }
==> List(A)
val chars = list collect { case c:Char => c }
==> List(c)
val ints = list collect { case i:Int => i }
==> List(2,4)
我能以某种方式一次完成这一切吗?我正在寻找:
val (strings, chars, ints) = list ??? {
case s:String => s
case c:Char => c
case i:Int => i
}
修改 忏悔 - 一个更接近我的实际用例的例子:
我有一个列表,我想根据某些条件进行分区:
val list2 = List("Word", " ", "", "OtherWord")
val (empties, whitespacesonly, words) = list2 ??? {
case s:String if s.isEmpty => s
case s:String if s.trim.isEmpty => s
case s:String => s
}
N.B。 partition
如果我只有2个案例(一个条件满足条件而另一个条件不满足),那么sys.exit
会很好。但是这里有多个条件要分开。
答案 0 :(得分:4)
基于您的第二个示例:您可以使用groupBy
和键控功能。我更喜欢将这些技术与discriminated union结合使用,以使代码的意图更加明显:
val list2 = List("Word", " ", "", "OtherWord")
sealed trait Description
object Empty extends Description
object Whitespaces extends Description
object Words extends Description
def strToDesc(str : String) : Description = str match {
case _ if str.isEmpty() => Empty
case _ if str.trim.isEmpty() => Whitespaces
case _ => Words
}
val descMap = (list2 groupBy strToDesc) withDefaultValue List.empty[String]
val (empties, whitespaceonly, words) =
(descMap(Empty),descMap(Whitespaces),descMap(Words))
如果您想稍后添加其他说明,则可以很好地扩展,例如AllCaps
...
答案 1 :(得分:3)
希望这有帮助:
list.foldLeft((List[String](), List[String](), List[String]())) {
case ((e,s,w),str:String) if str.isEmpty => (str::e,s,w)
case ((e,s,w),str:String) if str.trim.isEmpty => (e,str::s,w)
case ((e,s,w),str:String) => (e,s,str::w)
case (acc, _) => acc
}
答案 2 :(得分:2)
您可以使用partition
两次:
def partitionWords(list: List[String]) = {
val (emptyOrSpaces, words) = list.partition(_.trim.isEmpty)
val (empty, spaces) = emptyOrSpaces.partition(_.isEmpty)
(empty, spaces, words)
}
这给出了你的例子:
partitionWords(list2)
// (List(""),List(" "),List(Word, OtherWord))
通常,您可以将foldLeft
与元组一起用作累加器。
def partitionWords2(list: List[String]) = {
val nilString = List.empty[String]
val (empty, spaces, words) = list.foldLeft((nilString, nilString, nilString)) {
case ((empty, spaces, words), elem) =>
elem match {
case s if s.isEmpty => (s :: empty, spaces, words)
case s if s.trim.isEmpty => (empty, s :: spaces, words)
case s => (empty, spaces, s :: words)
}
}
(empty.reverse, spaces.reverse, words.reverse)
}
这会给你相同的结果。
答案 3 :(得分:1)
尾递归方法,
def partition(list: List[Any]): (List[Any], List[Any], List[Any]) = {
@annotation.tailrec
def inner(map: Map[String, List[Any]], innerList: List[Any]): Map[String, List[Any]] = innerList match {
case x :: xs => x match {
case s: String => inner(insertValue(map, "str", s), xs)
case c: Char => inner(insertValue(map, "char", c), xs)
case i: Int => inner(insertValue(map, "int", i), xs)
}
case Nil => map
}
def insertValue(map: Map[String, List[Any]], key: String, value: Any) = {
map + (key -> (value :: map.getOrElse(key, Nil)))
}
val partitioned = inner(Map.empty[String, List[Any]], list)
(partitioned.get("str").getOrElse(Nil), partitioned.get("char").getOrElse(Nil), partitioned.get("int").getOrElse(Nil))
}
val list1 = List("A", 2, 'c', 4)
val (strs, chars, ints) = partition(list1)
答案 4 :(得分:0)
根据@ Nyavro的回答,我结束了这个:
val list2 = List("Word", " ", "", "OtherWord")
val(empties, spaces, words) =
list2.foldRight((List[String](), List[String](), List[String]())) {
case (str, (e, s, w)) if str.isEmpty => (str :: e, s, w)
case (str, (e, s, w)) if str.trim.isEmpty => (e, str :: s, w)
case (str, (e, s, w)) => (e, s, str :: w)
}
==> empties: List[String] = List("")
==> spaces: List[String] = List(" ")
==> words: List[String] = List(Word, OtherWord)
我理解使用foldRight
的风险:主要是为了在正确的情况下启动,运行时需要递归,这可能会在大量输入上烧掉堆栈。但是,我的投入很小,这种风险是可以接受的。
话虽如此,如果有一个快速的方法_.reverse
三个我没有想过的元组列表,我全都听见了。
全部谢谢!