如何在丢弃集合的某些元素时进行groupBy

Ben*_*mes 8 scala

我想基于类型的鉴别器将序列分组到序列图中Option,类似于groupBy方法的结果但是None丢弃了导致的值.或者,可能通过PartialFunction鉴别器进行分组并丢弃未定义部分功能的那些.

这是一个具体的例子:

我有一组名称和一组命名空间.一些(但不是全部)名称属于有效的名称空间,我想将那些名称分组到一个Map中,丢弃那些没有名称的名称.

目前我的解决方案相当于:

val names = List("ns1.foo", "ns2.bar", "ns2.baz", "froznit")
val namespaces = List("ns1", "ns2")

def findNamespace(n: String): Option[String] = namespaces.find(n.startsWith)

val groupedNames = names.groupBy(findNamespace).collect {
  case (Some(ns), name) => (ns, name)
}
// Map((ns1,List(ns1.foo)), (ns2,List(ns2.bar, ns2.baz)))
Run Code Online (Sandbox Code Playgroud)

我对这个解决方案的关注是,使用names.groupBy(findNamespace),我正在创建一个中间Map,其中包含我不关心的所有名称None.如果我丢弃的名称数量变大,则该解决方案变得不那么有吸引力.

我试图避免这种情况有点像火车残骸,但是:

val groupedNames =
  names.
    map(n => (findNamespace(n), n)).
    collect({ case (Some(ns), n) => (ns, n) }).
    groupBy(_._1).
    map({ case (ns, names) => (ns, names.map(_._2)) })
Run Code Online (Sandbox Code Playgroud)

如果你以更聪明的方式解决这个问题,它会是什么?


编辑:理想情况下,解决方案应该只findNamespace(name)为每个名称调用一次,并仅使用Option[String]值构建Map ,而不调用单独的hasNamespace(name)谓词.

Aar*_*rup 6

避免收集丢弃名称的一种方法是使用flatMap:

names.flatMap(n => findNamespace(n) map (ns => (ns, n)))
   .groupBy(_._1) 
   .map { case (ns, pairs) => (ns, pairs map (_._2)) }
Run Code Online (Sandbox Code Playgroud)

你可以通过理解来实现同样的目标:

(for (n <- names; ns <- findNamespace(n)) yield (ns, n))
   .groupBy(_._1)
   .map { case (ns, pairs) => (ns, pairs map (_._2)) }
Run Code Online (Sandbox Code Playgroud)


huy*_*hjl 4

您可以使用 FoldLeft:

val gn = names.foldLeft(Map[String, List[String]]()){ case (acc, name) =>
  findNamespace(name) match { 
    case Some(ns) => acc + (ns -> (name :: acc.get(ns).getOrElse(Nil)))
    case _ => acc
  }
}
Run Code Online (Sandbox Code Playgroud)

假设顺序无关紧要,否则您可以使用 反转值gn.mapValues(_.reverse)