Scala, ¿cómo puedo contar el número de ocurrencias en una lista?

99
val list = List(1,2,4,2,4,7,3,2,4)

Quiero implementarlo así: list.count(2)(devuelve 3).

Gatspy
fuente
No sé si existe una forma adecuada de obtener el tamaño de una lista en scala, pero para su situación, podría usar una secuencia.
Qusay Fantazia
¿Sigue esta pregunta sin respuesta? Preguntando porque es posible que se haya olvidado de aceptar uno.
Tobias Kolb

Respuestas:

150

Una versión algo más limpia de una de las otras respuestas es:

val s = Seq("apple", "oranges", "apple", "banana", "apple", "oranges", "oranges")

s.groupBy(identity).mapValues(_.size)

dando un Mapcon un recuento para cada elemento en la secuencia original:

Map(banana -> 1, oranges -> 3, apple -> 3)

La pregunta pregunta cómo encontrar el recuento de un artículo específico. Con este enfoque, la solución requeriría mapear el elemento deseado a su valor de conteo de la siguiente manera:

s.groupBy(identity).mapValues(_.size)("apple")
ohruunuruus
fuente
2
¿Qué es "identidad"?
Igorock
4
Es la función de identidad, como se analiza aquí . La función groupByrequiere una función que aplique a elementos para que sepa cómo agruparlos. Una alternativa a agrupar las cadenas en la respuesta por sus identidades podría ser, por ejemplo, agrupar por su longitud ( groupBy(_.size)) o por su primera letra ( groupBy(_.head)).
ohruunuruus
2
El inconveniente es que se crean muchas colecciones inútiles (porque solo se necesita el tamaño).
Yann Moisan
¿Qué pasa si quisiera definir un mapa acumulador en esa expresión en lugar de crear un nuevo mapa?
Tobias Kolb
128

Las colecciones de Scala tienen count:list.count(_ == 2)

xiefei
fuente
48

Tuve el mismo problema que Sharath Prabhal y obtuve otra solución (para mí más clara):

val s = Seq("apple", "oranges", "apple", "banana", "apple", "oranges", "oranges")
s.groupBy(l => l).map(t => (t._1, t._2.length))

Con como resultado:

Map(banana -> 1, oranges -> 3, apple -> 3)
KWA
fuente
44
Una versión algo más limpia ess.groupBy(identity).mapValues(_.size)
ohruunuruus
1
@ohruunuruus esto debería ser una respuesta (vs comentario); Me encantaría votar con entusiasmo, si lo fuera (y seleccionarla como la mejor respuesta si yo fuera el OP);
doug
1
@doug algo nuevo en SO y no estaba seguro, pero feliz de complacer
ohruunuruus
27
list.groupBy(i=>i).mapValues(_.size)

da

Map[Int, Int] = Map(1 -> 1, 2 -> 3, 7 -> 1, 3 -> 1, 4 -> 3)

Tenga en cuenta que puede reemplazar (i=>i)con la identityfunción incorporada:

list.groupBy(identity).mapValues(_.size)
noego
fuente
Me encantan las soluciones breves que utilizan bibliotecas integradas
Rustam Aliyev
14
val list = List(1, 2, 4, 2, 4, 7, 3, 2, 4)
// Using the provided count method this would yield the occurrences of each value in the list:
l map(x => l.count(_ == x))

List[Int] = List(1, 3, 3, 3, 3, 1, 1, 3, 3)
// This will yield a list of pairs where the first number is the number from the original list and the second number represents how often the first number occurs in the list:
l map(x => (x, l.count(_ == x)))
// outputs => List[(Int, Int)] = List((1,1), (2,3), (4,3), (2,3), (4,3), (7,1), (3,1), (2,3), (4,3))
AndreasScheinert
fuente
1
pero da el num. ocurrencias para cada valor tantas veces como ocurre el valor; parece ineficiente y no muy útil ...
Erik Kaplun
13

Comenzando Scala 2.13, el método groupMapReduce lo hace en una pasada a través de la lista:

// val seq = Seq("apple", "oranges", "apple", "banana", "apple", "oranges", "oranges")
seq.groupMapReduce(identity)(_ => 1)(_ + _)
// immutable.Map[String,Int] = Map(banana -> 1, oranges -> 3, apple -> 3)
seq.groupMapReduce(identity)(_ => 1)(_ + _)("apple")
// Int = 3

Esta:

  • groups elementos de lista (grupo parte del grupo MapReduce)

  • maps cada ocurrencia de valor agrupado a 1 (parte del mapa del grupo Reducción del mapa )

  • reduces valores dentro de un grupo de valores ( _ + _) sumándolos (reducir parte de groupMap Reducir ).

Esta es una versión de un solo paso de lo que se puede traducir por:

seq.groupBy(identity).mapValues(_.map(_ => 1).reduce(_ + _))
Xavier Guihot
fuente
Bien, esto es lo que estaba buscando, me pareció triste que incluso los flujos de Java (que no son buenos en algunos aspectos) permitan esto en una sola pasada, mientras que Scala no pudo.
Dici
9

Me encontré con el mismo problema, pero quería contar varios elementos de una vez.

val s = Seq("apple", "oranges", "apple", "banana", "apple", "oranges", "oranges")
s.foldLeft(Map.empty[String, Int]) { (m, x) => m + ((x, m.getOrElse(x, 0) + 1)) }
res1: scala.collection.immutable.Map[String,Int] = Map(apple -> 3, oranges -> 3, banana -> 1)

https://gist.github.com/sharathprabhal/6890475

Sharath Prabhal
fuente
tal vez usar Streamy la respuesta aceptada le dará su objetivo de "una vez" más un código más claro.
juanchito
Esta solución itera la Lista solo una vez, usando groupBy y luego map lo hará dos veces.
ruloweb
7

Si quieres usarlo como list.count(2)tienes que implementarlo usando una clase implícita .

implicit class Count[T](list: List[T]) {
  def count(n: T): Int = list.count(_ == n)
}

List(1,2,4,2,4,7,3,2,4).count(2)  // returns 3
List(1,2,4,2,4,7,3,2,4).count(5)  // returns 0
LRLucena
fuente
7

Respuesta corta:

import scalaz._, Scalaz._
xs.foldMap(x => Map(x -> 1))

Respuesta larga:

Usando Scalaz , dado.

import scalaz._, Scalaz._

val xs = List('a, 'b, 'c, 'c, 'a, 'a, 'b, 'd)

luego todos estos (en el orden de menos simplificado a más simplificado)

xs.map(x => Map(x -> 1)).foldMap(identity)
xs.map(x => Map(x -> 1)).foldMap()
xs.map(x => Map(x -> 1)).suml
xs.map(_ -> 1).foldMap(Map(_))
xs.foldMap(x => Map(x -> 1))

rendimiento

Map('b -> 2, 'a -> 3, 'c -> 2, 'd -> 1)
Erik Kaplun
fuente
6

Es interesante notar que el mapa con el valor 0 predeterminado, diseñado intencionalmente para este caso, demuestra el peor desempeño (y no tan conciso como groupBy)

    type Word = String
    type Sentence = Seq[Word]
    type Occurrences = scala.collection.Map[Char, Int]

  def woGrouped(w: Word): Occurrences = {
        w.groupBy(c => c).map({case (c, list) => (c -> list.length)})
  }                                               //> woGrouped: (w: forcomp.threadBug.Word)forcomp.threadBug.Occurrences

  def woGetElse0Map(w: Word): Occurrences = {
        val map = Map[Char, Int]()
        w.foldLeft(map)((m, c) => m + (c -> (m.getOrElse(c, 0) + 1)) )
  }                                               //> woGetElse0Map: (w: forcomp.threadBug.Word)forcomp.threadBug.Occurrences

  def woDeflt0Map(w: Word): Occurrences = {
        val map = Map[Char, Int]().withDefaultValue(0)
        w.foldLeft(map)((m, c) => m + (c -> (m(c) + 1)) )
  }                                               //> woDeflt0Map: (w: forcomp.threadBug.Word)forcomp.threadBug.Occurrences

  def dfltHashMap(w: Word): Occurrences = {
        val map = scala.collection.immutable.HashMap[Char, Int]().withDefaultValue(0)
        w.foldLeft(map)((m, c) => m + (c -> (m(c) + 1)) )
    }                                             //> dfltHashMap: (w: forcomp.threadBug.Word)forcomp.threadBug.Occurrences

    def mmDef(w: Word): Occurrences = {
        val map = scala.collection.mutable.Map[Char, Int]().withDefaultValue(0)
        w.foldLeft(map)((m, c) => m += (c -> (m(c) + 1)) )
  }                                               //> mmDef: (w: forcomp.threadBug.Word)forcomp.threadBug.Occurrences

    val functions = List("grp" -> woGrouped _, "mtbl" -> mmDef _, "else" -> woGetElse0Map _
    , "dfl0" -> woDeflt0Map _, "hash" -> dfltHashMap _
    )                                  //> functions  : List[(String, String => scala.collection.Map[Char,Int])] = Lis
                                                  //| t((grp,<function1>), (mtbl,<function1>), (else,<function1>), (dfl0,<functio
                                                  //| n1>), (hash,<function1>))


    val len = 100 * 1000                      //> len  : Int = 100000
    def test(len: Int) {
        val data: String = scala.util.Random.alphanumeric.take(len).toList.mkString
        val firstResult = functions.head._2(data)

        def run(f: Word => Occurrences): Int = {
            val time1 = System.currentTimeMillis()
            val result= f(data)
            val time2 = (System.currentTimeMillis() - time1)
            assert(result.toSet == firstResult.toSet)
            time2.toInt
        }

        def log(results: Seq[Int]) = {
                 ((functions zip results) map {case ((title, _), r) => title + " " + r} mkString " , ")
        }

        var groupResults = List.fill(functions.length)(1)

        val integrals = for (i <- (1 to 10)) yield {
            val results = functions map (f => (1 to 33).foldLeft(0) ((acc,_) => run(f._2)))
            println (log (results))
                groupResults = (results zip groupResults) map {case (r, gr) => r + gr}
                log(groupResults).toUpperCase
        }

        integrals foreach println

    }                                         //> test: (len: Int)Unit


    test(len)
    test(len * 2)
// GRP 14 , mtbl 11 , else 31 , dfl0 36 , hash 34
// GRP 91 , MTBL 111

    println("Done")
    def main(args: Array[String]) {
    }

produce

grp 5 , mtbl 5 , else 13 , dfl0 17 , hash 17
grp 3 , mtbl 6 , else 14 , dfl0 16 , hash 16
grp 3 , mtbl 6 , else 13 , dfl0 17 , hash 15
grp 4 , mtbl 5 , else 13 , dfl0 15 , hash 16
grp 23 , mtbl 6 , else 14 , dfl0 15 , hash 16
grp 5 , mtbl 5 , else 13 , dfl0 16 , hash 17
grp 4 , mtbl 6 , else 13 , dfl0 16 , hash 16
grp 4 , mtbl 6 , else 13 , dfl0 17 , hash 15
grp 3 , mtbl 5 , else 14 , dfl0 16 , hash 16
grp 3 , mtbl 6 , else 14 , dfl0 16 , hash 16
GRP 5 , MTBL 5 , ELSE 13 , DFL0 17 , HASH 17
GRP 8 , MTBL 11 , ELSE 27 , DFL0 33 , HASH 33
GRP 11 , MTBL 17 , ELSE 40 , DFL0 50 , HASH 48
GRP 15 , MTBL 22 , ELSE 53 , DFL0 65 , HASH 64
GRP 38 , MTBL 28 , ELSE 67 , DFL0 80 , HASH 80
GRP 43 , MTBL 33 , ELSE 80 , DFL0 96 , HASH 97
GRP 47 , MTBL 39 , ELSE 93 , DFL0 112 , HASH 113
GRP 51 , MTBL 45 , ELSE 106 , DFL0 129 , HASH 128
GRP 54 , MTBL 50 , ELSE 120 , DFL0 145 , HASH 144
GRP 57 , MTBL 56 , ELSE 134 , DFL0 161 , HASH 160
grp 7 , mtbl 11 , else 28 , dfl0 31 , hash 31
grp 7 , mtbl 10 , else 28 , dfl0 32 , hash 31
grp 7 , mtbl 11 , else 28 , dfl0 31 , hash 32
grp 7 , mtbl 11 , else 28 , dfl0 31 , hash 33
grp 7 , mtbl 11 , else 28 , dfl0 32 , hash 31
grp 8 , mtbl 11 , else 28 , dfl0 31 , hash 33
grp 8 , mtbl 11 , else 29 , dfl0 38 , hash 35
grp 7 , mtbl 11 , else 28 , dfl0 32 , hash 33
grp 8 , mtbl 11 , else 32 , dfl0 35 , hash 41
grp 7 , mtbl 13 , else 28 , dfl0 33 , hash 35
GRP 7 , MTBL 11 , ELSE 28 , DFL0 31 , HASH 31
GRP 14 , MTBL 21 , ELSE 56 , DFL0 63 , HASH 62
GRP 21 , MTBL 32 , ELSE 84 , DFL0 94 , HASH 94
GRP 28 , MTBL 43 , ELSE 112 , DFL0 125 , HASH 127
GRP 35 , MTBL 54 , ELSE 140 , DFL0 157 , HASH 158
GRP 43 , MTBL 65 , ELSE 168 , DFL0 188 , HASH 191
GRP 51 , MTBL 76 , ELSE 197 , DFL0 226 , HASH 226
GRP 58 , MTBL 87 , ELSE 225 , DFL0 258 , HASH 259
GRP 66 , MTBL 98 , ELSE 257 , DFL0 293 , HASH 300
GRP 73 , MTBL 111 , ELSE 285 , DFL0 326 , HASH 335
Done

¡Es curioso que lo más conciso groupBysea ​​más rápido que incluso un mapa mutable!

Val
fuente
3
Sospecho un poco de este punto de referencia, ya que no está claro cuál es el tamaño de los datos. La groupBysolución realiza una toLowerpero las otras no. También por qué utilizar una coincidencia de patrón para el mapa - uso justo mapValues. Así que junte eso y obtendrá def woGrouped(w: Word): Map[Char, Int] = w.groupBy(identity).mapValues(_.size): pruébelo y verifique el rendimiento para listas de varios tamaños. Finalmente en las otras soluciones, ¿por qué a) declarar mapyb) convertirlo en var ?? Solo hazlow.foldLeft(Map.empty[Char, Int])...
samthebest
1
Gracias por proporcionar más datos (cambió mi voto :). Creo que la razón por la que la implementación de groupBy usa un mapa mutable de Builders que están optimizados para incrementos iterativos. Luego convierte el mapa mutable en uno inmutable usando un MapBuilder. Probablemente también haya alguna evaluación perezosa bajo el capó para hacer las cosas más rápidas.
samthebest
@samthebest Simplemente busca el contador e incrementalo. No veo qué se puede almacenar en caché allí. El caché debe ser un mapa del mismo tipo de todos modos.
Val
No digo que almacene nada. Imagino que el aumento de rendimiento proviene del uso de Builders, y posiblemente de alguna evaluación perezosa.
samthebest
@samthebest evaluación perezosa = evaluación retrasada (llamada por nombre) + almacenamiento en caché. No se puede hablar de evaluación perezosa pero no de almacenamiento en caché.
Val
4

No obtuve el tamaño de la lista usando, lengthsino más bien, sizecomo una de las respuestas anteriores, lo sugirió debido al problema que se informa aquí .

val list = List("apple", "oranges", "apple", "banana", "apple", "oranges", "oranges")
list.groupBy(x=>x).map(t => (t._1, t._2.size))
edkeveked
fuente
3

Aquí tienes otra opción:

scala> val list = List(1,2,4,2,4,7,3,2,4)
list: List[Int] = List(1, 2, 4, 2, 4, 7, 3, 2, 4)

scala> list.groupBy(x => x) map { case (k,v) => k-> v.length }
res74: scala.collection.immutable.Map[Int,Int] = Map(1 -> 1, 2 -> 3, 7 -> 1, 3 -> 1, 4 -> 3)
Akavall
fuente
3
scala> val list = List(1,2,4,2,4,7,3,2,4)
list: List[Int] = List(1, 2, 4, 2, 4, 7, 3, 2, 4)

scala> println(list.filter(_ == 2).size)
3
balu
fuente
3

usando gatos

import cats.implicits._

"Alphabet".toLowerCase().map(c => Map(c -> 1)).toList.combineAll
"Alphabet".toLowerCase().map(c => Map(c -> 1)).toList.foldMap(identity)
Sergii Shevchyk
fuente
2
¡Vaya, 4 iteraciones a través de la secuencia original! Incluso seq.groupBy(identity).mapValues(_.size)solo pasa dos veces.
WeaponsGrade
El número de iteraciones puede no importar para una cadena pequeña como "Alfabeto", pero cuando se trata de millones de elementos en una colección, ¡las iteraciones ciertamente importan!
WeaponsGrade
2

Prueba esto, debería funcionar.


val list = List(1,2,4,2,4,7,3,2,4)
list.count(_==2) 

Regresará 3

datos dcripse
fuente
¿En qué se diferencia esto de la respuesta de xiefei dada hace siete años?
jwvh
0

Aquí tienes una forma bastante sencilla de hacerlo.

val data = List("it", "was", "the", "best", "of", "times", "it", "was", 
                 "the", "worst", "of", "times")
data.foldLeft(Map[String,Int]().withDefaultValue(0)){
  case (acc, letter) =>
    acc + (letter -> (1 + acc(letter)))
}
// => Map(worst -> 1, best -> 1, it -> 2, was -> 2, times -> 2, of -> 2, the -> 2)
Jim Newton
fuente