web-dev-qa-db-ja.com

Scala)で2つのリストを組み合わせる

List[(Int, String)の形式の2つのリストから:

l1 = List((1,"a"),(3,"b"))
l2 = List((3,"a"),(4,"c"))

Integersが同じであるStringsを組み合わせて、この3番目のリストを取得するにはどうすればよいですか。

l3 = List((4,"a"),(3,"b"),(4,"c"))

現在、両方のリストをトラバースして、文字列が同じかどうかを追加していますが、パターンマッチングを使用した簡単な解決策があるはずです。

17
Samuel Thomas
val l = l1 ::: l2
val m = Map[String, Int]()
(m /: l) {
  case (map, (i, s)) => { map.updated(s, i + (map.get(s) getOrElse 0))}
}.toList // Note: Tuples are reversed.

しかし、updatedの部分を実行するためのよりエレガントな方法があると思います。

20
Debilski

どうですか、

(l1 ++ l2).groupBy(_._2).mapValues(_.unzip._1.sum).toList.map(_.swap)

REPLでこれを少し解凍すると、何が起こっているかを示すのに役立ちます。

scala> l1 ++ l2
res0: List[(Int, Java.lang.String)] = List((1,a), (3,b), (3,a), (4,c))

scala> res0.groupBy(_._2)
res1: ... = Map(c -> List((4,c)), a -> List((1,a), (3,a)), b -> List((3,b)))

scala> res1.mapValues(_.unzip)
res2: ... = Map(c -> (List(4),List(c)), a -> (List(1, 3),List(a, a)), b -> (List(3),List(b)))                         

scala> res1.mapValues(_.unzip._1)                                                                                                                                                                      
res3: ... = Map(c -> List(4), a -> List(1, 3), b -> List(3))                                                                                    

scala> res1.mapValues(_.unzip._1.sum)
res4: ... = Map(c -> 4, a -> 4, b -> 3)                                                                                                               

scala> res4.toList                                                                                                                                                                                     
res5: List[(Java.lang.String, Int)] = List((c,4), (a,4), (b,3))                                                                                                                                        

scala> res5.map(_.swap)
res6: List[(Int, Java.lang.String)] = List((4,c), (4,a), (3,b))
20
Miles Sabin

Scalaz の場合、これは簡単です。

import scalaz._
import Scalaz._

val l3 = (l1.map(_.swap).toMap |+| l2.map(_.swap).toMap) toList

|+|メソッドは、Semigroup[T]の実装が存在するすべてのタイプTで公開されます。そして、たまたまMap[String, Int]の半群がまさにあなたが望むものであるということです。

10
Apocalisp
for ( (k,v) <- (l1++l2).groupBy(_._2).toList ) yield ( v.map(_._1).sum, k )
1
AmigoNico

このソリューションでは、リストが2回トラバースされることに注意してください。

val l3 = (l1 Zip l2).foldRight(List[(Int, String)]()) {
  case ((firstPair @ (firstNumber, firstWord),
        secondPair @ (secondNumber, secondWord)),
        result) =>
    if (firstWord == secondWord)
      ((firstNumber + secondNumber), firstWord) :: result
    else
      firstPair :: secondPair :: result
}
0
agilesteel
val a = List(1,1,1,0,0,2)
val b = List(1,0,3,2)

scala> List.concat(a,b)
res31: List[Int] = List(1, 1, 1, 0, 0, 2, 1, 0, 3, 2)

(or) 

scala> a.:::(b)
res32: List[Int] = List(1, 0, 3, 2, 1, 1, 1, 0, 0, 2)

(or) 

scala> a ::: b
res28: List[Int] = List(1, 1, 1, 0, 0, 2, 1, 0, 3, 2)
0
KARTHIKEYAN.A

別の不透明 1疑わしい効率でありながら、紛れもない有効性の2つのライナー:

val lst = l1 ++ l2
lst.map(_._2).distinct.map(i => (lst.filter(_._2 == i).map(_._1).sum, i))
0
Luigi Plinge

Miles Sabinの答えScala 2.13の新しい groupMapReduce メソッドを使用する代わりに、(その名前が示すように)groupByとそれに続くmapValuesと同等の(より効率的な)メソッドです。 reduceステップ:

(l1 ::: l2).groupMapReduce(_._2)(_._1)(_ + _).toList.map(_.swap)
// List[(Int, String)] = List((3,b), (4,a), (4,c))

この:

  • l1l2の前に追加します

  • 2番目のタプル部分に基づくgroups要素(groupMapReduceのグループ部分)

  • mapsは、値を最初のタプル部分にグループ化しました(groupMapReduceのマップ部分)

  • reduces値(_ + _)を合計して(groupMapReduceの一部を削減)

  • そして最後にswapsタプルの部分。

これは同等のバージョンです 1回のパスで実行されます (グループ/マップ/リデュース部分の場合)次のリストを介して:

(l1 ::: l2).groupBy(_._2).mapValues(_.map(_._1).reduce(_ + _)).toList.map(_.swap)
0
Xavier Guihot