Scala, jak mogę policzyć liczbę wystąpień na liście


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

Chcę to zaimplementować w ten sposób: list.count(2)(zwraca 3).


Nie wiem, czy istnieje właściwy sposób uzyskania rozmiaru listy w scali, ale w twojej sytuacji możesz użyć sekwencji.
Qusay Fantazia,

Czy to pytanie nadal pozostaje bez odpowiedzi? Pytanie, ponieważ mogłeś zapomnieć o przyjęciu.
Tobias Kolb,

Odpowiedzi:


150

Nieco czystsza wersja jednej z pozostałych odpowiedzi to:

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

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

dając Mapz liczbą dla każdego elementu w oryginalnej sekwencji:

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

Pytanie dotyczy tego, jak znaleźć liczbę określonej pozycji. Przy takim podejściu rozwiązanie wymagałoby odwzorowania żądanego elementu na jego wartość licznika w następujący sposób:

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

2
czym jest „tożsamość”?
Igorock

4
Jest to funkcja tożsamości, o której tutaj mowa . Funkcja groupBywymaga funkcji, którą stosuje do elementów, aby wiedziała, jak je grupować. Alternatywą dla grupowania ciągów w odpowiedzi według ich tożsamości może być, powiedzmy, grupowanie według ich długości ( groupBy(_.size)) lub pierwszej litery ( groupBy(_.head)).
ohruunuruus

2
Wadą jest to, że powstaje wiele bezużytecznych kolekcji (ponieważ potrzebny jest tylko rozmiar).
Yann Moisan

co jeśli chciałbym zdefiniować mapę akumulatorów w tym wyrażeniu zamiast tworzyć nową mapę?
Tobias Kolb,


48

Miałem ten sam problem co Sharath Prabhal i dostałem inne (dla mnie jaśniejsze) rozwiązanie:

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

W rezultacie:

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

45
Nieco czystszą wersją jests.groupBy(identity).mapValues(_.size)
ohruunuruus

1
@ohruunuruus to powinna być odpowiedź (a komentarz); chciałbym entuzjastycznie zagłosować za, gdyby tak było (i wybrałbym to jako najlepszą odpowiedź, gdybym był OP);
Doug

1
@doug nieco nowy w SO i nie był pewien, ale z przyjemnością zobowiązuję się
ohruunuruus

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

daje

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

Pamiętaj, że możesz zastąpić (i=>i)wbudowaną identityfunkcją:

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

uwielbiam krótkie rozwiązania wykorzystujące wbudowane biblioteki
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
ale daje num. wystąpienia dla każdej wartości tyle razy, ile występuje - wydaje się nieefektywne i niezbyt przydatne ...
Erik Kaplun

14

Zaczynając Scala 2.13, metoda groupMapReduce robi to w jednym przejściu przez listę:

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

To:

  • groupElementy listy s (część grupowa grupy MapReduce)

  • maps każde wystąpienie wartości zgrupowanej do 1 (część mapy grupy Map Reduce)

  • reduces wartości w grupie wartości ( _ + _), sumując je (zmniejsz część groupMap Reduce ).

To jest jednoprzebiegowa wersja tego, co można przetłumaczyć za pomocą:

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

Fajnie, właśnie tego szukałem, było mi smutno, że nawet strumienie Java (które nie są dobre w niektórych aspektach) pozwalają na to w jednym przebiegu, podczas gdy Scala nie.
Dici

9

Napotkałem ten sam problem, ale chciałem policzyć wiele elementów za jednym razem.

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


być może użycie Streami zaakceptowana odpowiedź przyniesie cel „jednorazowo” i jaśniejszy kod.
juanchito

To rozwiązanie iteruje Listę tylko raz, używając groupBy, a następnie map zrobi to dwukrotnie.
ruloweb

7

Jeśli chcesz go używać, tak jak list.count(2)musisz zaimplementować go przy użyciu klasy niejawnej .

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

Krótka odpowiedź:

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

Długa odpowiedź:

Używając Scalaz , dane.

import scalaz._, Scalaz._

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

to wszystkie te (w kolejności od mniej uproszczonego do bardziej uproszczonego)

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

wydajność

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

6

Warto zauważyć, że mapa z domyślną wartością 0, celowo zaprojektowana dla tego przypadku, wykazuje najgorszą wydajność (i nie jest tak zwięzła 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]) {
    }

produkuje

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

Ciekawe, że najbardziej zwięzłe groupByjest szybsze niż nawet zmienna mapa!


3
Jestem trochę podejrzliwy w stosunku do tego testu porównawczego, ponieważ nie jest jasne, jaki jest rozmiar danych. groupByRozwiązanie wykonuje toLowerale inni nie. Po co też używać dopasowania wzorca do mapy - po prostu użyj mapValues. Więc skręć to razem i otrzymaj def woGrouped(w: Word): Map[Char, Int] = w.groupBy(identity).mapValues(_.size)- spróbuj tego i sprawdź wydajność dla różnych list rozmiarów. Wreszcie w innych rozwiązaniach, dlaczego a) deklarować mapib) czynić z var ?? Po prostu zróbw.foldLeft(Map.empty[Char, Int])...
samthebest

1
Dziękuję za podanie większej ilości danych (zmieniłem mój głos :). Myślę, że powodem jest to, że implementacja groupBy wykorzystuje zmienną mapę Builders, która jest zoptymalizowana pod kątem iteracyjnych przyrostów. Następnie konwertuje zmienną mapę na niezmienną przy użyciu MapBuilder. Prawdopodobnie pod maską dzieje się też leniwa ocena, aby przyspieszyć.
samthebest

@samthebest Wystarczy spojrzeć na licznik i zwiększyć go. Nie widzę, co można tam zapisać w pamięci podręcznej. Pamięć podręczna i tak musi być mapą tego samego rodzaju.
Val

Nie mówię, że cokolwiek buforuje. Wyobrażam sobie, że wzrost wydajności wynika z użycia Builders i prawdopodobnie leniwej oceny.
samthebest

@samthebest leniwa ocena = opóźniona ocena (wywołanie według nazwy) + buforowanie. Nie można mówić o leniwej ocenie, ale nie o buforowaniu.
Val

4

Nie otrzymałem rozmiaru listy za pomocą, lengthale raczej sizejako jedna z powyższych odpowiedzi zasugerowała to z powodu zgłoszonego tutaj problemu .

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

3

Oto inna opcja:

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

za pomocą kotów

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 iteracje oryginalnej sekwencji! Nawet seq.groupBy(identity).mapValues(_.size)przechodzi tylko dwa razy.
WeaponsGrade

Liczba iteracji może nie mieć znaczenia w przypadku małego ciągu, takiego jak „Alphabet”, ale w przypadku milionów elementów w kolekcji iteracje z pewnością mają znaczenie!
Klasa broni

2

Spróbuj tego, powinno działać.


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

Zwróci 3


1
Czym różni się to od odpowiedzi udzielonej siedem lat temu przez xiefei?
jwvh

0

Oto całkiem prosty sposób na zrobienie tego.

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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.