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 names and values this separate object:
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. Is there any better way to solve the problem?
Turn each Seq into its own 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. 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 :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With