将 2 个自定义类型的向量合并为 return 1 个向量

Combine 2 vectors of a custom type to return 1 vector

我有一个 class:

case class Custom(label: String, num: Long)

给定这两个列表:

val l1 = Vector(Custom("a", 1), Custom("aa", 1))
val l2 = Vector(Custom("a", 1))

我希望获得结果列表:

val l3 = Vector(Custom("a", 2), Custom("aa", 1))

我试过像这样使用折叠:

l1.foldLeft(l2)((acc: List[Custom], el: Custom) => {
   val itemWithIndex: Option[(Custom, Int)] = acc.zipWithIndex.find(_._1.label == el.label)
   itemWithIndex match {
     case Some(x) => acc.updated(x._2, Custom(el.label, acc(x._2).num + el.num))
     case None => el :: acc
   }
})

此实现迭代累加器 (l2) 3 次,l1 一次。我正在寻找更有效的解决方案。

Brian 提出了一个很好的建议,可以像这样简单地组合列表:

(l1 ++ l2)
  .groupBy(_.label)
  .map { 
    case (label, customs) => Custom(label, customs.map(_.num).sum)
  }

groupBy 给我们一个 Map[String, List[Custom]],其中 List[Custom] 是具有相同标签的所有 Custom 对象。剩下的就是总结 List,并为每个标签创建一个新的 Custom 对象。

由于这是归约,所以可以做成Monoid:

implicit def customMonoid = new Monoid[Vector[Custom]] {
  override def append(l1: Vector[Custom], l2: => Vector[Custom]): Vector[Custom] =
    (l1 ++ l2)
      .groupBy(_.label)
      .map {
        case (a, b) => Custom(a, b.map(_.num).sum)
      }.toVector

  override def zero: Vector[Custom] = Vector.empty[Custom]
}
 import scalaz.syntax.monoid._
  case class Custom(label: String, num: Long)
  val l1 = Vector(Custom("a", 1), Custom("aa", 1))
  val l2 = Vector(Custom("a", 1))

  def toM(v: Vector[Custom]): Map[String, Long] = {
    (v map (cus ⇒ cus.label → cus.num)).toMap
  }

  def fromM(m: Map[String, Long]): Vector[Custom] = {
    m.map{case (l , num) ⇒ Custom(l, num)}.toVector
  }

  implicit val myMonoid = new Monoid[Vector[Custom]] {
    import scalaz.std.anyVal.longInstance
    val mm = scalaz.std.map.mapMonoid[String, Long]

    override def zero: Vector[Custom] = fromM(mm.zero)

    override def append(f1: Vector[Custom], f2: ⇒ Vector[Custom]): Vector[Custom] = fromM(mm.append(toM(f1), toM(f2)))
  }



  println(l1 |+| l2) \Vector(Custom(a,2), Custom(aa,1))

如果想更清楚,可以把toMfromM提取到Isomorphism实例中。