2016-08-23 52 views
1

我有一個類:結合自定義類型的2個向量,返回1矢量

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一次。我正在尋找更有效的解決方案。

+2

很酷。你有什麼嘗試? – Jubobs

+0

累加器起始位置爲第二個列表的foldLeft。然後,對於第一個列表中的每個項目,檢查它是否存在,是否存在,添加值,否則將值作爲新元素插入。這不是非常有效。遍歷整個列表以檢查是否存在某個東西,然後再次獲取它的位置。 – soote

+1

編輯你的問題,並在那裏解釋你的方法;讓你的問題自成一體。 – Jubobs

回答

1
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到同構的實例。

+0

非常乾淨,有沒有什麼辦法可以將這個或類似的抽象應用於具有> 2個參數的案例類? – soote

+1

當然,這是可以實現的不成形。你可以將任何case類轉換成'HList'並返回,並且爲它定義'Monoid'並不難 - http://stackoverflow.com/questions/37363337/deriving-hlist-of-zeroes-from-a-型的-hlist-的-類羣 –

2

布賴恩給了一個很好的建議前手簡單地將列表,像這樣:

(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] 
} 
+1

你也可以在'Custom'上定義'def add(b:Custom):Custom = copy(num = num + b.num)',然後你上面'case'語句的主體可以被'customs reduce(_ add _)' – handler

+0

非常好的建議,我一定會用這個改進來實現它。 – soote

+0

現在看看它,我認爲它在地圖中創建的自定義對象的可讀性略高。由於這個函數只能在'Vector [Custom]'上運行,所以我認爲我沒有足夠的抽象Custom的add函數來證明輕微的可讀性。 – soote