Scala come posso contare il numero di occorrenze in una lista


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

Voglio implementarlo in questo modo: list.count(2)(restituisce 3).


Non so se esiste un modo corretto per ottenere la dimensione di una lista in scala, ma per la tua situazione potresti usare una sequenza.
Qusay Fantazia

Questa domanda è ancora senza risposta? Chiedere perché potresti aver dimenticato di accettarne uno.
Tobias Kolb

Risposte:


150

Una versione un po 'più pulita di una delle altre risposte è:

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

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

dando una Mapcon un conteggio per ogni elemento nella sequenza originale:

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

La domanda chiede come trovare il conteggio di un elemento specifico. Con questo approccio, la soluzione richiederebbe la mappatura dell'elemento desiderato al suo valore di conteggio come segue:

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

2
cos'è l '"identità"?
Igorock

4
È la funzione di identità, come discusso qui . La funzione groupByrichiede una funzione che si applica agli elementi in modo che sappia come raggrupparli. Un'alternativa al raggruppamento delle stringhe nella risposta in base alla loro identità potrebbe essere, ad esempio, raggruppare in base alla lunghezza ( groupBy(_.size)) o alla prima lettera ( groupBy(_.head)).
ohruunuruus

2
Lo svantaggio è che vengono create molte raccolte inutili (perché è necessaria solo la dimensione).
Yann Moisan

e se volessi definire una mappa accumulatore in quell'espressione invece di creare una nuova mappa?
Tobias Kolb

128

le collezioni scala hanno count:list.count(_ == 2)


48

Ho avuto lo stesso problema di Sharath Prabhal e ho ottenuto un'altra soluzione (per me più chiara):

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

Con come risultato:

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

44
Una versione un po 'più pulita ès.groupBy(identity).mapValues(_.size)
ohruunuruus

1
@ohruunuruus questa dovrebbe essere una risposta (vs commento); mi piacerebbe votare con entusiasmo, se lo fosse (e selezionarlo come la migliore risposta se fossi l'OP);
Doug

1
@doug un po 'nuovo in SO e non ne ero sicuro, ma felice di accontentarlo
ohruunuruus

27
list.groupBy(i=>i).mapValues(_.size)

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

Nota che puoi sostituire (i=>i) con la identityfunzione incorporata:

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

adoro le soluzioni brevi che utilizzano le librerie integrate
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))

1
ma dà il num. occorrenze per ogni valore tante volte quante il valore si verifica — sembra inefficiente e non molto utile ...
Erik Kaplun

13

A partire Scala 2.13, il metodo groupMapReduce lo fa in un passaggio attraverso l'elenco:

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

Questo:

  • groups elementi dell'elenco (parte del gruppo del gruppo MapReduce)

  • maps ciascuna occorrenza di valore raggruppato su 1 (mappa parte del gruppo Riduzione mappa )

  • reduces valori all'interno di un gruppo di valori ( _ + _) sommandoli (riduci parte di groupMap Riduci ).

Questa è una versione one-pass di ciò che può essere tradotto da:

seq.groupBy(identity).mapValues(_.map(_ => 1).reduce(_ + _))

Bello, questo è quello che stavo cercando, ho trovato triste che anche i flussi Java (che non sono buoni in alcuni aspetti) lo permettessero in un unico passaggio mentre Scala non poteva.
Dici

9

Ho riscontrato lo stesso problema ma volevo contare più elementi in una volta sola ..

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


forse usando Streame la risposta accettata produrrà il tuo obiettivo di "una volta" più un codice più chiaro.
juanchito

Questa soluzione itera l'Elenco una sola volta, usando groupBy e poi map lo farà due volte.
ruloweb

7

Se vuoi usarlo come list.count(2)devi implementarlo usando una classe implicita .

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

7

Risposta breve:

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

Risposta lunga:

Usando Scalaz , dato.

import scalaz._, Scalaz._

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

poi tutti questi (nell'ordine da meno semplificato a più semplificato)

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

dare la precedenza

Map('b -> 2, 'a -> 3, 'c -> 2, 'd -> 1)

6

È interessante notare che la mappa con valore predefinito 0, intenzionalmente progettata per questo caso, mostra le prestazioni peggiori (e non così concise come 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

È curioso che il più conciso groupBysia più veloce persino della mappa mutevole!


3
Sono un po 'sospettoso di questo benchmark in quanto non è chiaro quale sia la dimensione dei dati. La groupBysoluzione esegue un toLowerma gli altri no. Anche perché usare una corrispondenza di pattern per la mappa - basta usare mapValues. Quindi arrotolali insieme e ottieni def woGrouped(w: Word): Map[Char, Int] = w.groupBy(identity).mapValues(_.size): provalo e controlla le prestazioni per vari elenchi di dimensioni. Infine nelle altre soluzioni, perché a) dichiarare mapeb) renderlo una var ?? Basta farew.foldLeft(Map.empty[Char, Int])...
samthebest

1
Grazie per aver fornito più dati (cambiato il mio voto :). Penso che il motivo per cui l'implementazione di groupBy utilizzi una mappa mutabile di Builders ottimizzata per incrementi iterativi. Quindi converte la mappa mutabile in una immutabile utilizzando un file MapBuilder. Probabilmente c'è anche qualche pigra valutazione in corso sotto il cofano per rendere le cose più veloci.
samthebest

@samthebest Basta cercare il contatore e incrementarlo. Non vedo cosa può essere memorizzato nella cache. La cache deve comunque essere una mappa dello stesso tipo.
Val

Non sto dicendo che memorizza nulla nella cache. Immagino che l'aumento delle prestazioni derivi dall'uso di Builders, e forse da qualche valutazione pigra.
samthebest

@samthebest lazy evaluation = valutazione ritardata (chiamata per nome) + memorizzazione nella cache. Non si può parlare di valutazione pigra ma non di memorizzazione nella cache.
Val

4

Non ho ottenuto la dimensione dell'elenco utilizzando lengthma piuttosto sizecome una risposta sopra suggerita a causa del problema riportato qui .

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

3

Ecco un'altra opzione:

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)

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

3

usando i gatti

import cats.implicits._

"Alphabet".toLowerCase().map(c => Map(c -> 1)).toList.combineAll
"Alphabet".toLowerCase().map(c => Map(c -> 1)).toList.foldMap(identity)

2
Wow, 4 iterazioni nella sequenza originale! Anche seq.groupBy(identity).mapValues(_.size)solo due volte.
WeaponsGrade

Numero di iterazioni non può importa per una piccola stringa come "alfabeto", ma quando si tratta di milioni di elementi in una raccolta, iterazioni certamente fanno importa!
WeaponsGrade

2

Prova questo, dovrebbe funzionare.


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

Tornerà 3


In che modo questo differisce dalla risposta data da xiefei sette anni fa?
jwvh

0

Ecco un modo abbastanza semplice per farlo.

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.