[英]Merge two Seq to create a Map
I have an object such as: 我有一个对象,例如:
case class Person(name: String, number: Int)
And two Sequences of this object: 和这个对象的两个序列:
Seq(("abc", 1), ("def", 2))
Seq(("abc", 300), ("xyz", 400))
I want to merge these two sequences in a single Map whose key is the name
s and values this separate object: 我想将这两个序列合并在一个Map中,其键是
name
s并且为这个单独的对象赋值:
case class CombineObject(firstNumber: Option[Int], secondNumber: Option[Int])
So that my final map would look like: 所以我的最终地图看起来像:
Map(
"abc" -> CombineObject(Some(1), Some(300)),
"def" -> CombineObject(Some(2), None)),
"xyz" -> CombineObject(None, Some(400))
)
All I can think here is to run 2 for loops over the sequence to create the map. 我在这里想到的就是在序列上运行2 for循环来创建地图。 Is there any better way to solve the problem?
有没有更好的方法来解决这个问题?
Turn each Seq
into its own Map
. 将每个
Seq
转换为自己的Map
。 After that it's pretty easy. 之后,这很容易。
case class Person( name : String
, number : Int )
val s1 = Seq(Person("abc",1),Person("def",2))
val s2 = Seq(Person("abc",300),Person("xyz",400))
val m1 = s1.foldLeft(Map.empty[String,Int]){case (m,p) => m+(p.name->p.number)}
val m2 = s2.foldLeft(Map.empty[String,Int]){case (m,p) => m+(p.name->p.number)}
case class CombineObject( firstNumber : Option[Int]
, secondNumber : Option[Int] )
val res = (m1.keySet ++ m2.keySet).foldLeft(Map.empty[String,CombineObject]){
case (m,k) => m+(k -> CombineObject(m1.get(k),m2.get(k)))
}
//res: Map[String,CombineObject] = Map(abc -> CombineObject(Some(1),Some(300))
// , def -> CombineObject(Some(2),None)
// , xyz -> CombineObject(None,Some(400)))
This assumes that each Seq
has no duplicate name
entries. 这假设每个
Seq
没有重复的name
条目。 It's not obvious how that situation should be handled. 应该如何处理这种情况并不明显。
Another proposal with a recursive function. 另一个具有递归函数的提议。 First, it sorts both lists by key then does the processing.
首先,它按键对两个列表进行排序然后进行处理。
case class Person(
name: String,
number: Int
)
case class CombineObject(
firstNumber : Option[Int],
secondNumber : Option[Int]
)
val left = List(Person("abc", 1), Person("def", 2))
val right = List(Person("abc", 300), Person("xyz", 400))
def merge(left: List[Person], right: List[Person]): Map[String, CombineObject] = {
@tailrec
def doMerge(left: List[Person], right: List[Person], acc: Map[String, CombineObject] = Map.empty): Map[String, CombineObject] = {
(left, right) match {
case(Person(name1, number1) :: xs, Person(name2, number2) :: ys) =>
if(name1 == name2) {
doMerge(xs, ys, acc + (name1 -> CombineObject(Some(number1), Some(number2))))
} else {
doMerge(xs, ys, acc + (name2 -> CombineObject(None, Some(number2))) + (name1 -> CombineObject(Some(number1), None)))
}
//if both lists are always same size, next two cases are not needed
case (Nil, Person(name2, number2) :: ys) =>
doMerge(Nil, ys, acc + (name2 -> CombineObject(None, Some(number2))) )
case (Person(name1, name2) :: xs, Nil) =>
doMerge(xs, Nil, acc + (name1 -> CombineObject(None, Some(name2))))
case _ => acc
}
}
doMerge(left.sortBy(_.name), right.sortBy(_.name))
}
merge(left, right) //Map(xyz -> (None,Some(400)), def -> (Some(2),None), abc -> (Some(1),Some(300)))
Looks kind of scary :) 看起来有点可怕:)
Another potential variation 另一个潜在的变化
case class Person(name : String, number : Int)
case class CombineObject(firstNumber : Option[Int], secondNumber : Option[Int])
val s1 = Seq(Person("abc",1),Person("def",2))
val s2 = Seq(Person("abc",300),Person("xyz",400))
(s1.map(_-> 1) ++ s2.map(_ -> 2))
.groupBy { case (person, seqTag) => person.name }
.mapValues {
case List((Person(name1, number1), _), (Person(name2, number2), _)) => CombineObject(Some(number1), Some(number2))
case List((Person(name, number), seqTag)) => if (seqTag == 1) CombineObject(Some(number), None) else CombineObject(None, Some(number))
case Nil => CombineObject(None, None)
}
which outputs 哪个输出
res1: Map[String,CombineObject] = Map(abc -> CombineObject(Some(1),Some(300)), xyz -> CombineObject(None,Some(400)), def -> CombineObject(Some(2),None)
Yet another solution, probably arguable :) ... 又一个解决方案,可能是有争议的:) ...
import scala.collection.immutable.TreeMap
case class CombineObject(firstNumber : Option[Int], secondNumber : Option[Int])
case class Person(name : String,number : Int)
val seq1 = Seq(Person("abc",1),Person("def",2))
val seq2 = Seq(Person("abc",300),Person("xyz",400))
def toExhaustiveMap(seq1:Seq[Person], seq2:Seq[Person]) = TreeMap(
seq1.map { case Person(s, i) => s -> Some(i) }: _*
) ++ ((seq2.map(_.name) diff seq1.map(_.name)).map(_ -> None))
val result = (toExhaustiveMap(seq1,seq2) zip toExhaustiveMap(seq2,seq1)).map {
case ((name1, number1), (_, number2)) => name1 -> CombineObject(number1, number2)
}
println(result)
Map(abc -> CombineObject(Some(1),Some(300)), def -> CombineObject(Some(2),None), xyz -> CombineObject(None,Some(400)))
Map(abc - > CombineObject(Some(1),Some(300)),def - > CombineObject(Some(2),None),xyz - > CombineObject(None,Some(400)))
Hope it helps. 希望能帮助到你。
Yet another alternative, if performance isn't a priority: 另一种选择,如果表现不是优先事项:
// val seq1 = Seq(("abc", 1), ("def", 2))
// val seq2 = Seq(("abc", 300), ("xyz", 400))
(seq1 ++ seq2)
.toMap
.keys
.map(k => k -> CombineObject(
seq1.collectFirst { case (`k`, v) => v },
seq2.collectFirst { case (`k`, v) => v }
))
.toMap
// Map(
// "abc" -> CombineObject(Some(1), Some(300)),
// "def" -> CombineObject(Some(2), None),
// "xyz" -> CombineObject(None, Some(400))
// )
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.