Scala:按案例分区(不是通过过滤器)

时间:2015-11-11 15:13:10

标签: scala

我有一个混合值列表:

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会很好。但是这里有多个条件要分开。

5 个答案:

答案 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三个我没有想过的元组列表,我全都听见了。

全部谢谢!