首页 文章

Scala集合用于在维护订单的同时进行分组

提问于
浏览
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的顺序 . 因此,带有workId 1的Job出现在workId 3的作业之前, Map 中的条目在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然后映射的额外迭代 .

使用标准收集方法是否有更惯用和简洁的方法?

1 回答

  • 1

    与List处理的几乎所有问题一样,这可以通过折叠来解决!

    (for {
      job <- jobs.view;
      user <- job.users
    } yield (job, user)).foldLeft (Map[String, Vector[Int]]()) { case (acc, (a,b)) =>
      acc + (b -> (acc.getOrElse(b, Vector()) :+ a.workId))
    }
    

    不幸的是,Scala的类型推理器无法确定初始“Map”的类型,因此您必须明确指定它 .

    在初始集合上使用'view'方法会使这个变得懒惰,并且只会执行初始列表中的单个传递 .

相关问题