我想基于Option
类型的鉴别器将序列分组到序列图中,类似于groupBy
方法的结果,但其中的值导致{{1}被丢弃了。或者,也许,通过None
鉴别器进行分组并丢弃未定义部分功能的那些。
这是一个具体的例子:
我有一组名称和一组命名空间。一些(但不是全部)名称属于有效的名称空间,我想将那些名称分组到一个Map中,丢弃那些没有名称的名称。
目前我的解决方案相当于:
PartialFunction
我对此解决方案的关注是,使用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)))
,我正在创建一个中间地图,其中包含我不关心的所有名称,位于密钥names.groupBy(findNamespace)
下。如果我丢弃的名字数量变大,这个解决方案变得不那么有吸引力了。
我试图避免这种情况有点像火车残骸,但是:
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)) })
一次,并仅使用findNamespace(name)
值构建地图,而不调用单独的Option[String]
谓词。
答案 0 :(得分:6)
避免收集丢弃名称的一种方法是使用flatMap
:
names.flatMap(n => findNamespace(n) map (ns => (ns, n)))
.groupBy(_._1)
.map { case (ns, pairs) => (ns, pairs map (_._2)) }
你可以用for-comprehension来实现同样的目标:
(for (n <- names; ns <- findNamespace(n)) yield (ns, n))
.groupBy(_._1)
.map { case (ns, pairs) => (ns, pairs map (_._2)) }
答案 1 :(得分:4)
我不确定toMap
的效率如何,但将该选项置于for-comprehension中至少可以避免收集None
结果:
scala> val m = (for { n <- names; ns <- findNamespace(n) } yield n -> ns).toMap
m: scala.collection.immutable.Map[java.lang.String,String] = Map(ns1.foo -> ns1, ns2.bar -> ns2, ns2.baz -> ns2)
scala> val groupedNames = m.keys.groupBy(m)
groupedNames: scala.collection.immutable.Map[String,Iterable[java.lang.String]] = Map(ns1 -> Set(ns1.foo), ns2 -> Set(ns2.bar, ns2.baz))
答案 2 :(得分: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
}
}
假设订单无关紧要,您可以使用gn.mapValues(_.reverse)
反转值。
答案 3 :(得分:2)
我想出了一个关于huynhjl答案的变体,将match
替换为map
:
val gn = (Map[String, List[String]]() /: names) { (acc, name) =>
acc ++ findNamespace(name).map(ns => ns -> (name :: acc.getOrElse(ns, Nil)))
}
答案 4 :(得分:0)
我建议“首先过滤,然后使用groupBy”方法,如下所示:
scala> val names = List("ns1.foo", "ns2.bar", "ns2.baz", "froznit")
names: List[java.lang.String] = List(ns1.foo, ns2.bar, ns2.baz, froznit)
scala> val namespaces = List("ns1", "ns2")
namespaces: List[java.lang.String] = List(ns1, ns2)
scala> names filter { n => namespaces exists { n startsWith _ } } groupBy { _ take 3 }
res1: scala.collection.immutable.Map[String,List[java.lang.String]] = Map(ns1 -> List(ns1.foo), ns2 -> List(ns2.bar, ns2.baz))