2016-08-23 6 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)) 

私はこのようにfoldを使用しようとしました:

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を1回反復します。私はより効率的なソリューションを探しています。

+2

クール。何を試しましたか? – Jubobs

+0

foldLeftここで、アキュムレータは2番目のリストとして開始します。次に、最初のリストの各項目について、存在するかどうかをチェックし、存在する場合は値を追加し、そうでない場合は新しい要素として値を挿入します。あまり効率的ではありません。何かが存在するかどうかを確認するためにリスト全体を反復し、そして再びその位置を得るために再び繰り返す。 – 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

確かに、それは無形で達成可能です。どのようなケースクラスも 'HList'に変換して戻すことができます。それは' Monoid'を定義するのが難しくありません。http://stackoverflow.com/questions/37363337/deriving-hlist-of-zeroes-from-a-モナドの種類 –

2

は、ブライアンは、単純にそうように手の前にリストを組み合わせるための素敵な提案を与えた:

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

groupByは私たちにList[Custom]が同じラベルを持つすべてのオブジェクトCustomあるMap[String, List[Custom]]を与えます。残ってすべてのことはListことをまとめると、各ラベルについては、この合計と新しい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] 
} 
+1

'Custom'で' def add(b:Custom):Custom = copy(num = num + b.num) 'を定義することもできます。そして、' case'文の本体は 'customs reduce _) ' – handler

+0

非常に良い提案、私は間違いなくその改善でそれを実装するでしょう。 – soote

+0

これを見ると、マップ内にカスタムオブジェクトが作成されていると少し読みやすくなっています。この関数は 'Vector [Custom] 'でしか動作しないので、読みやすさのわずかなヒットを正当化するためにCustomのadd関数を十分に抽象化しているとは思えません。 – soote

関連する問題