3
我有这样的事情斯卡拉集合分组,同时维持秩序
case class Job(workId: Int, users: List[String])
val jobs = IndexedSeq(Job(1, List("a", "b")), Job(2, List("b", "c")), Job(3, List("a", "c")), Job(4, List("d", "b")))
我想将其转换为类似:
Map(c -> Vector(2, 3), a -> Vector(1, 3), d -> Vector(4), b -> Vector(1, 2, 4))
我基本上要保持Job.workId的顺序原始序列。因此,由于JobId 1的作业在workId 3的作业之前,因此地图中的条目在JobId 3之前具有JobId 1。
我无法找到这样做的简单方法。现在,我有:
((for (job <- jobs;
user <- job.users)
yield { (user, job.work) }) groupBy { tuple => tuple._1 }) map { tuple => (tuple._1 -> (tuple._2 map { _._2 })) }
这首先创建:
Map(c -> Vector((c,2), (c,3)), a -> Vector((a,1), (a,3)), d -> Vector((d,4)), b -> Vector((b,1), (b,2), (b,4)))
,然后将其转换为:
Map(c -> Vector(2, 3), a -> Vector(1, 3), d -> Vector(4), b -> Vector(1, 2, 4))
这似乎相当冗长。我想知道是否有更简单的方法来做到这一点,同时保持秩序。另外我不喜欢它需要多次迭代初始序列。
我还有一个冗长的解决方案:
val mapping = scala.collection.mutable.Map[String, IndexedSeq[Int]]()
for (job <- jobs;
user <- job.users)
yield{
if (mapping.contains(user)) {
val entry = mapping(user)
mapping.put(user, entry :+ job.work)
} else {
mapping += user -> mutable.IndexedSeq(job.work)
}
}
映射现在是:
Map(c -> ArrayBuffer(2, 3), a -> ArrayBuffer(1, 3), d -> ArrayBuffer(4), b -> ArrayBuffer(1, 2, 4))
这股最初的推导,但并不需要是来自使用GROUPBY然后地图额外的迭代。
有没有一个更标准的收集方法这样做的地道和简洁的方式?
谢谢。实际上,我用一些不同的方法来写一点性能测试。 foldLeft方法比我之前的命令式方法表现得更好。我修改了命令式方法来使用视图,然后对结果运行foreach。最后,我也有一个方法,只是使用嵌套的foreach。嵌套的foreach表现最好,但折叠方法非常接近。势在必行的方法表现得更糟,视图修改的执行情况稍好一些。 – Rajiv 2013-04-11 21:38:12
我想我最终会使用Guava的ArrayListMultimap。它具有最佳性能,并且与Scala的multimap不同,它也保持插入的顺序。 – Rajiv 2013-04-11 23:53:36