如果你不关心结果列表排序(所以基本上你的结果是Set
),它是那样简单:
def sum(a: Cart, b: Cart) = {
//require(a.userId == b.userId)
a.copy(Qty = a.Qty + b.Qty)
}
(userCart ++ guestCart)
.groupBy(x => x.ProductId -> x.SellerId)
.mapValues(_.reduce(sum _))
.values
.toList //toSet is more appropriate here
结果:
List(Cart(900,4,2,1), Cart(900,2,2,4), Cart(900,1,1,4), Cart(901,3,3,2))
(! )请注意,如果发生碰撞,我只需要第一个userId
(请参阅sum
函数)。但是,如果这是隐含的,它会保持用户优先于用户。
扯到一个Set
,这个结果等于你的要求:
scala> val mRes = List(Cart(900,4,2,1), Cart(900,2,2,4), Cart(900,1,1,4), Cart(901,3,3,2))
mRes: List[Cart] = List(Cart(900,4,2,1), Cart(900,2,2,4), Cart(900,1,1,4), Cart(901,3,3,2))
scala> val req = List(Cart(900,2,2,4),Cart(900,1,1,4),Cart(901,3,3,2),Cart(900,4,2,1))
req: List[Cart] = List(Cart(900,2,2,4), Cart(900,1,1,4), Cart(901,3,3,2), Cart(900,4,2,1))
scala> mRes.toSet == req.toSet
res17: Boolean = true
说明:
++
连接两个列表
groupBy
组值由一些谓词(如x.ProductId -> x.SellerId
这相当于你的案例中的一个元组(x.ProductId, x.SellerId)
)。它保留了组内的顺序,但组本身没有排序 - 这就是为什么在结果列表中的顺序未定义。操作员在列表返回Map[Key, List[Value]]
,你的情况Map[(Int, Int), List[Cart]]
mapValues
迭代与车
reduce
内mapValues
使用sum
功能
减少列表与求和车车,我没有重新附加具有唯一(x.ProductId, x.SellerId)
的对象,因为它们仅仅表示为带有一个元素的列表,所以reduce
函数没有触及它们 - 它只返回第一个(也是唯一)元素。
a.copy(Qty = ...)
使得a
副本修改Qty
领域。在我们的例子中,我将左元素作为模板,因此在选择userId
时,优先于(userCart ++ guestCart)
的元素将具有更高的优先级。
回答标题的问题大约减去两组:
scala> Set(1,2,3,4) - 4
res16: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
scala> Set(1,2,3,4) -- Set(3,4)
res15: scala.collection.immutable.Set[Int] = Set(1, 2)
如果集合元素是case类(假设hashCode
/equals
方法并没有覆盖)的情况下 - 这将比较所有字段以检查两个元素之间的相等性。
还有就是groupBy
解决方案与一组理论的理论联系。首先,你可以很容易地注意到我的解决方案可以用SQL的GROUP BY
+ AGGREGATE
(groupBy
和reduce
-在Scala中的变形)表示。 SQL主要基于关系代数,而关系代数又部分基于集合论,所以就是这样。
P.S.按照惯例,scala中的字段/值/变量名应始终以小写字母开头。第一个大写字母表示常数。
当组合通用元素时,如果'userId'值不匹配,结果'userId'如何决定? – jwvh
如果userId不匹配,则要求始终在最终列表中显示userCart的userId。 – user7384606