合并两个Seq以创建一个Map

时间:2019-06-11 16:26:21

标签: scala dictionary

我有一个对象,例如:

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

此对象的两个序列:

Seq(("abc", 1), ("def", 2))
Seq(("abc", 300), ("xyz", 400))

我想将这两个序列合并到一个键为name的Map中,并对该单独的对象赋值:

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

这样我的最终地图将如下所示:

Map(
  "abc" -> CombineObject(Some(1), Some(300)),
  "def" -> CombineObject(Some(2), None)),
  "xyz" -> CombineObject(None,    Some(400))
)

我能想到的是在序列上运行2个for循环以创建地图。有没有更好的方法来解决问题?

5 个答案:

答案 0 :(得分:5)

将每个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)))

这假设每个Seq没有重复的name条目。这种情况应该如何处理还不清楚。

答案 1 :(得分:3)

另一个具有递归功能的建议。首先,它按键对两个列表进行排序,然后进行处理。

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

看起来有点吓人:)

答案 2 :(得分:3)

另一个潜在的变化

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)

答案 3 :(得分:2)

还有另一种解决方案,可能有争议:) ...

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

希望有帮助。

答案 4 :(得分:2)

如果性能不是优先考虑的话,请选择另一种方法:

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