Scala 组合自定义类型的2个向量以返回1个向量

Scala 组合自定义类型的2个向量以返回1个向量,scala,scalaz,Scala,Scalaz,我有一门课: 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[Custo

我有一门课:

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
   }
})
(l1 ++ l2)
  .groupBy(_.label)
  .map { 
    case (label, customs) => Custom(label, customs.map(_.num).sum)
  }

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

Brian提出了一个很好的建议,即把列表合并在一起,如下所示:

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
   }
})
(l1 ++ l2)
  .groupBy(_.label)
  .map { 
    case (label, customs) => Custom(label, customs.map(_.num).sum)
  }
groupBy
为我们提供了一个
Map[String,List[Custom]]
其中
List[Custom]
是所有具有相同标签的
Custom
对象。剩下的就是对
列表
进行汇总,并为每个标签创建一个新的
自定义
对象

因为这是一个约化,所以它可以被构造成一个幺半群:

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]
}

如果您想更清楚,可以将
toM
fromM
提取到同构实例。

Cool。你试过什么?折叠式折叠,累加器作为第二个列表开始。然后,对于第一个列表中的每个项目,检查它是否存在,如果存在,则添加值,否则将值作为新元素插入。这不是很有效。迭代整个列表以检查是否存在某些内容,然后再次获取其位置;让你的问题变得独立。用缓慢的实现更新答案这很有趣,不是靠你自己解决的,所以我将提供一些帮助,告诉你我将如何解决这个问题。组合两个列表以形成一个新列表,然后按
a
对组合列表进行分组,并使用第一个元素将每个分组的项目映射到一个新的
自定义
类,并计算第二个元素的大小。希望有帮助。你也可以在
Custom
上定义
def add(b:Custom):Custom=copy(num=num+b.num)
然后你上面的
案例的主体可以是
Custom reduce(uuu添加)
非常好的建议,我肯定会用改进来实现它。现在看看,我认为,在地图中创建自定义对象时,它的可读性稍高一些。由于此函数仅在
Vector[Custom]
上运行,因此我认为对Custom的add函数的抽象不够,无法证明对可读性的轻微影响。我同意您应该选择可读性更好的函数。从我的角度很难说,我假设
Custom
只是为了这篇文章的目的而编造的?如果不知道实际的类型是什么,我就无法决定是否有一个
add
方法属于它。真正的实现是完全不同的。但是,如果groupBy字段和add函数都被抽象出来,那么这类
mergeByField
函数似乎是可重用的。非常干净,有没有办法将此抽象或类似的抽象应用于参数大于2的case类?当然,它是可以用shapeless实现的。您可以将任何case类转换为
HList
并返回,这并不难为它定义
Monoid