scala - scala - 合并两个序列以创建映射

我有一个对象,例如:


case class Person(name: String, number: Int)



此对象的两个序列:


Seq(("abc", 1), ("def", 2))


Seq(("abc", 300), ("xyz", 400))



我希望将这两个序列合并在一个映射中,它键为name s和该单独的对象:


case class CombineObject(firstNumber: Option[Int], secondNumber: Option[Int])



这样我的最终map看起来就像


Map(


"abc" -> CombineObject(Some(1), Some(300)),


"def" -> CombineObject(Some(2), None)),


"xyz" -> CombineObject(None, Some(400))


)



有什么更好的方法来解决这个问题?

时间:

把每个Seq变成它自己的Map ,在那之后很容易。


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)))



递归函数的另一个建议,首先,它按键排序两个列表,然后进行处理。


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)))



看起来有点吓人

另一个解决方案,可能是有争议的:)。


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)) )

希望它能帮上忙。

另一个潜在的变种


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)


 }



输出


res1: Map[String,CombineObject] = Map(abc -> CombineObject(Some(1),Some(300)), xyz -> CombineObject(None,Some(400)), def -> CombineObject(Some(2),None)



如果不追求性能,则另一种方法是:


// 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))


// )



...