switch objects for comprehension classes list scala pattern-matching

objects - Combinando dos listas en Scala



switch case in scala (8)

De 2 listas del formulario List[(Int, String) :

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

¿Cómo puedo combinar los Integer donde las String son iguales para obtener esta tercera lista?

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

Ahora mismo estoy recorriendo ambas listas y agregando si las cadenas son iguales, pero creo que debería haber una solución simple con la coincidencia de patrones.


Con Scalaz , esto es muy fácil.

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

El |+| El método está expuesto en todos los tipos T para los cuales existe una implementación de Semigroup[T] . Y da la casualidad de que el semigrupo para Map[String, Int] es exactamente lo que quieres.


Otro opaco de dos líneas de eficacia cuestionable y eficacia indudable:

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


Qué tal si,

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

Descomprimir esto un poco en el REPL ayuda a mostrar lo que está pasando,

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))


Tenga en cuenta que con esta solución, las listas se recorren dos veces.

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 }


Una alternativa a la respuesta de Miles Sabin utilizando el nuevo método groupMapReduce Scala 2.13 que es (como su nombre lo indica) un equivalente (más eficiente) de un groupBy seguido de mapValues y un paso de reduce :

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

Esta:

  • antepone l1 a l2

  • Elementos del group basados ​​en su segunda parte de la tupla (parte del grupo del grupo MapReduce)

  • los valores agrupados del map en su primera parte de la tupla (parte del mapa del grupo Reducir mapa )

  • reduce los valores de s ( _ + _ ) sumándolos (reduzca parte de groupMap Reduce )

  • y finalmente swap las partes de las tuplas.

Esta es una versión equivalente realizada en una sola pasada (para el grupo / mapa / reducir parte) a través de la Lista de:

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


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


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)


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.

Pero supongo que hay una forma más elegante de hacer la parte updated .