Scala como posso contar o número de ocorrências em uma lista

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

Quero implementar assim: list.count(2)(retorna 3).

Gatspy
fonte
Não sei se existe uma maneira adequada de obter o tamanho de uma lista em scala, mas para a sua situação, você poderia usar uma sequência.
Qusay Fantazia
Esta pergunta ainda não foi respondida? Perguntar porque você pode ter esquecido de aceitar um.
Tobias Kolb

Respostas:

150

Uma versão um pouco mais limpa de uma das outras respostas é:

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

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

dando um Mapcom uma contagem para cada item na sequência original:

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

A questão pergunta como encontrar a contagem de um item específico. Com essa abordagem, a solução exigiria o mapeamento do elemento desejado para seu valor de contagem da seguinte maneira:

s.groupBy(identity).mapValues(_.size)("apple")
Ohruunuruus
fonte
2
o que é "identidade"?
Igorock
4
É a função de identidade, conforme discutido aqui . A função groupByrequer uma função que se aplica aos elementos para saber como agrupá-los. Uma alternativa para agrupar as strings na resposta por suas identidades poderia ser, digamos, agrupar por seu comprimento ( groupBy(_.size)) ou por sua primeira letra ( groupBy(_.head)).
ohruunuruus
2
A desvantagem é que muitas coleções inúteis (porque apenas o tamanho é necessário) são criadas.
Yann Moisan
e se eu quisesse definir um mapa de acumulador nessa expressão em vez de criar um novo mapa?
Tobias Kolb
128

coleções scala têm count:list.count(_ == 2)

xiefei
fonte
48

Tive o mesmo problema de Sharath Prabhal e consegui outra solução (para mim mais clara):

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

Com como resultado:

Map(banana -> 1, oranges -> 3, apple -> 3)
KWA
fonte
44
Uma versão um pouco mais limpa és.groupBy(identity).mapValues(_.size)
ohruunuruus
1
@ohruunuruus isso deve ser uma resposta (vs comentário); adoraria votar positivamente com entusiasmo, se fosse (e selecioná-la como a melhor resposta se eu fosse o OP);
doug
1
@doug um pouco novo no SO e não tinha certeza, mas feliz em obrigar
ohruunuruus
27
list.groupBy(i=>i).mapValues(_.size)

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

Observe que você pode substituir (i=>i)pela identityfunção integrada :

list.groupBy(identity).mapValues(_.size)
noego
fonte
adoro soluções curtas usando 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
fonte
1
mas produz o num. ocorrências para cada valor tantas vezes quanto o valor ocorre - parece ineficiente e não muito útil ...
Erik Kaplun
13

Começando Scala 2.13, o método groupMapReduce faz isso em uma passagem pela 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

Este:

  • groups elementos da lista (grupo parte do grupo MapReduce)

  • maps cada ocorrência de valor agrupado para 1 (parte do mapa do grupo Reduzir Mapa )

  • reduces valores dentro de um grupo de valores ( _ + _) somando-os (reduzir parte de groupMap Reduzir ).

Esta é uma versão de uma passagem do que pode ser traduzido por:

seq.groupBy(identity).mapValues(_.map(_ => 1).reduce(_ + _))
Xavier Guihot
fonte
Bom, isso é o que eu estava procurando, achei triste que até mesmo fluxos de Java (que não são bons em alguns aspectos) permitem isso em uma única passagem, enquanto Scala não podia.
Dici
9

Tive o mesmo problema, mas queria contar vários itens de uma 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
fonte
talvez usar Streame a resposta aceita resultará em seu objetivo de "uma tentativa" mais código mais claro.
juanchito de
Esta solução itera a Lista apenas uma vez, usando groupBy e, em seguida, map fará isso duas vezes.
ruloweb
7

Se você quiser usá-lo da mesma forma, list.count(2)terá que implementá-lo usando uma classe 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
fonte
7

Resposta curta:

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

Resposta longa:

Usando Scalaz , dado.

import scalaz._, Scalaz._

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

então, todos estes (da ordem de menos simplificado para mais 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))

produção

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

É interessante notar que o mapa com valor padrão 0, intencionalmente projetado para este caso, demonstra o pior desempenho (e não tão conciso quanto 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]) {
    }

produz

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

É curioso que mais conciso groupByseja mais rápido do que um mapa mutável!

Val
fonte
3
Desconfio um pouco desse benchmark, pois não está claro qual é o tamanho dos dados. A groupBysolução tem um desempenho, toLowermas as outras não. Além disso, por que usar uma correspondência de padrão para o mapa - basta usar mapValues. Então, junte tudo e você terá def woGrouped(w: Word): Map[Char, Int] = w.groupBy(identity).mapValues(_.size)- tente isso e verifique o desempenho para várias listas de tamanhos. Finalmente, nas outras soluções, por que a) declarar mapeb) torná-lo uma var ?? Basta fazerw.foldLeft(Map.empty[Char, Int])...
samthebest
1
Obrigado por fornecer mais dados (mudou meu voto :). Acho que a razão pela qual a implementação de groupBy usa um mapa mutável de Builders que são otimizados para incrementos iterativos. Em seguida, ele converte o mapa mutável em um imutável usando um MapBuilder. Provavelmente, há alguma avaliação preguiçosa acontecendo nos bastidores também para tornar as coisas mais rápidas.
samthebest
@samthebest Basta pesquisar o contador e incrementá-lo. Não vejo o que pode ser armazenado em cache lá. O cache precisa ser um mapa do mesmo tipo de qualquer maneira.
Val
Não estou dizendo que armazena nada em cache. Imagino que o aumento de desempenho venha do uso de Builders e, possivelmente, de alguma avaliação preguiçosa.
samthebest
@samthebest avaliação preguiçosa = avaliação atrasada (chamada por nome) + cache. Você não pode falar sobre avaliação preguiçosa, mas não sobre cache.
Val
4

Não obtive o tamanho da lista usando, lengthmas sim sizecomo uma das respostas acima sugeridas devido ao problema relatado aqui .

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

Aqui está outra opção:

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
fonte
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
fonte
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
fonte
2
Uau, 4 iterações na sequência original! Mesmo seq.groupBy(identity).mapValues(_.size)só passa duas vezes.
Weapons Grau
Número de iterações pode não importa para uma pequena string como "Alphabet", mas quando se lida com milhões de itens em uma coleção, iterações certamente fazer importa!
WeaponsGrade
2

Tente isso, deve funcionar.


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

Ele retornará 3

dados dcripse
fonte
Como isso difere da resposta de xiefei dada sete anos atrás?
jwvh
0

Esta é uma maneira bem fácil de fazer isso.

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
fonte