Scala根据另一个列表中的值对一个列表进行排序

Oph*_*lia 8 scala list

我有两个IndexedSeq

works[Work]
order[Int]
Run Code Online (Sandbox Code Playgroud)

每个对象Work都有一个带有整数值的id字段:Work.id在顺序列表中有一些id,它们按照我们需要整理工作的顺序排列.就像,在位置0中有第一个id,所以我们需要在工作数组中找到与此对应的id的工作,并将其放在0位置,依此类推.有没有办法用scala做这个而不经过两个循环?就像,一些优雅的方式?一些伪数据,例如:

order = 33, 22, 11, 55

works = (33, "some text"), (55, "eeeee"), (22, "fdsfs"), (11, "fdsffds")
Run Code Online (Sandbox Code Playgroud)

排序后:

order = 33, 22, 11, 55

works = (33, "some text"),(22, "fdsfs"),  (11, "fdsffds"), (55, "eeeee"),
Run Code Online (Sandbox Code Playgroud)

dk1*_*k14 7

您可以使用字典,复杂度为O(N)(与两个嵌套循环的N*N比较更好):

scala> val order = List(33, 22, 11, 55)
order: List[Int] = List(33, 22, 11, 55)

scala> val works = List((33, "some text"), (55, "eeeee"), (22, "fdsfs"), (11, "fdsffds"))
works: List[(Int, String)] = List((33,some text), (55,eeeee), (22,fdsfs), (11,fdsffds))

scala> val worksMap = works.toMap
worksMap: scala.collection.immutable.Map[Int,String] = Map(33 -> some text, 55 -> eeeee, 22 -> fdsfs, 11 -> fdsffds)

scala> val newWorks = order zip order.map(worksMap)
newWorks: List[(Int, String)] = List((33,some text), (22,fdsfs), (11,fdsffds), (55,eeeee))
Run Code Online (Sandbox Code Playgroud)

如果你的实体不仅仅是元组:

scala> val worksMap = (works map (_._1) zip works).toMap //any other key extraction, like `_.myKey` may be applied instead of `_._1`
worksMap: scala.collection.immutable.Map[Int,(Int, String)] = Map(33 -> (33,some text), 55 -> (55,eeeee), 22 -> (22,fdsfs), 11 -> (11,fdsffds))

scala> order.map(worksMap)
res13: List[(Int, String)] = List((33,some text), (22,fdsfs), (11,fdsffds), (55,eeeee))
Run Code Online (Sandbox Code Playgroud)

如果你不想花费内存Map- 只需使用find而不是Map.apply(但它会是O(N*N),所以它更慢):

scala> val newWorks = order.map(x => works.find(_._1 == x).get)
newWorks: List[(Int, String)] = List((33,some text), (22,fdsfs), (11,fdsffds), (55,eeeee))
Run Code Online (Sandbox Code Playgroud)

如果您不希望在order不包含密钥的情况下发生异常,您可以使用flatMap:

scala> val newWorks = order.flatMap(x => works.find(_._1 == x))
newWorks: List[(Int, String)] = List((33,some text), (22,fdsfs), (11,fdsffds), (55,eeeee))

scala> order.flatMap(worksMap.get)
res15: List[(Int, String)] = List((33,some text), (22,fdsfs), (11,fdsffds), (55,eeeee))
Run Code Online (Sandbox Code Playgroud)


Ben*_*ich 7

您可能对该sortWith方法感兴趣:

works.sortWith((a, b) => order.indexOf(a._1) < order.indexOf(b._1))
Run Code Online (Sandbox Code Playgroud)


Wal*_*fie 5

您可以按照与 相同的顺序将 转换Seq[Work]为 aMap[WorkId, Work]collect映射中的值Seq[WorkId]

例如:

case class Work(id: Int, text: String)
// defined class Work

val order = Seq(33, 22, 11, 55)
// order: Seq[Int] = List(33, 22, 11, 55)

val works = Seq(Work(33, "some text"), Work(55, "eeeee"), Work(22, "fdsfs"), Work(11, "fdsffds"))
// works: Seq[Work] = List(Work(33, "some text"), Work(55, "eeeee"), Work(22, "fdsfs"), Work(11, "fdsffds"))

val workMap = works.map(work => work.id -> work).toMap
// workMap: Map[Int, Work] = Map(33 -> Work(33, "some text"), 55 -> Work(55, "eeeee"), 22 -> Work(22, "fdsfs"), 11 -> Work(11, "fdsffds"))

order.collect(workMap)
// res14: Seq[Work] = List(Work(33, "some text"), Work(22, "fdsfs"), Work(11, "fdsffds"), Work(55, "eeeee"))
Run Code Online (Sandbox Code Playgroud)

这与其他答案类似,但使用collect,这是有效的,因为Map[A, B]也是PartialFunction[A, B].


但是,上述解决方案假定工作 ID 是唯一的。

如果工作 ID 不是唯一的,您可以使用groupByand flatMap

val works = Seq(Work(33, "a"), Work(34, "b"), Work(33, "c"), Work(35, "d"))
// works: Seq[Work] = List(Work(33, "a"), Work(34, "b"), Work(33, "c"), Work(35, "d"))

val worksMap = works.groupBy(_.id)
//worksMap: Map[Int, Seq[Work]] = Map(35 -> List(Work(35, "d")), 34 -> List(Work(34, "b")), 33 -> List(Work(33, "a"), Work(33, "c")))

val order = Seq(35, 34, 33)
//order: Seq[Int] = List(35, 34, 33)

order.flatMap(id => worksMap.getOrElse(id, Seq.empty))
// res23: Seq[Work] = List(Work(35, "d"), Work(34, "b"), Work(33, "a"), Work(33, "c"))
Run Code Online (Sandbox Code Playgroud)