2017-02-24 66 views
1

我已经case类以下如何在Scala中的两个列表之间执行集合论操作?

case class Cart(userId: Int, ProductId :Int, SellerId:Int, Qty: Int) 

我有以下列表:

val mergedCart :List[Cart]= List(Cart(900,1,1,2),Cart(900,2,2,2),Cart(901,3,3,2),Cart(901,2,2,2),Cart(901,1,1,2),Cart(900,4,2,1)) 

val userCart:List[Cart] = List(Cart(900,1,1,2),Cart(900,2,2,2),Cart(900,4,2,1)) 

val guestCart:List[Cart] = List(Cart(901,3,3,2),Cart(901,2,2,2),Cart(901,1,1,2)) 

val commonCart = List(Cart(900,2,2,4), Cart(900,1,1,4)) 

我的要求是,我必须得到下面的列表作为输出:

List(Cart(900,2,2,4),Cart(900,1,1,4),Cart(901,3,3,2),Cart(900,4,2,1)) 

最终列表应该具有来自userCartguestCart的共同对象,基于ProductIdSellerId组合和两个对象的数量被添加。然后,userCartguestCart中与通用对象不匹配的其他对象也应出现在输出的最终列表中。

我是新来的斯卡拉,我无法解决这个问题,请帮助我这个代码。

+0

当组合通用元素时,如果'userId'值不匹配,结果'userId'如何决定? – jwvh

+0

如果userId不匹配,则要求始终在最终列表中显示userCart的userId。 – user7384606

回答

2

如果你不关心结果列表排序(所以基本上你的结果是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迭代与车

  • reducemapValues使用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 + AGGREGATEgroupByreduce-在Scala中的变形)表示。 SQL主要基于关系代数,而关系代数又部分基于集合论,所以就是这样。


P.S.按照惯例,scala中的字段/值/变量名应始终以小写字母开头。第一个大写字母表示常数。

+0

非常感谢!这帮了我很多! – user7384606

相关问题