Scala bagaimana saya bisa menghitung jumlah kemunculan dalam daftar


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

Saya ingin menerapkannya seperti ini: list.count(2)(return 3).


Saya tidak tahu apakah ada cara yang tepat untuk mendapatkan ukuran daftar dalam skala, tetapi untuk situasi Anda, Anda dapat menggunakan urutan.
Qusay Fantazia

Apakah pertanyaan ini masih belum terjawab? Meminta karena Anda mungkin lupa menerimanya.
Tobias Kolb

Jawaban:


150

Versi yang lebih bersih dari salah satu jawaban lainnya adalah:

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

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

memberi Mapdengan hitungan untuk setiap item dalam urutan aslinya:

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

Pertanyaannya menanyakan bagaimana menemukan hitungan item tertentu. Dengan pendekatan ini, solusi akan membutuhkan pemetaan elemen yang diinginkan ke nilai hitungannya sebagai berikut:

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

2
apa itu "identitas"?
Igorock

4
Itu adalah fungsi identitas, seperti yang didiskusikan di sini . Fungsi tersebut groupBymembutuhkan fungsi yang diterapkan pada elemen sehingga mengetahui cara mengelompokkannya. Sebuah alternatif untuk mengelompokkan string dalam jawaban berdasarkan identitas mereka bisa jadi, katakanlah, pengelompokan berdasarkan panjangnya ( groupBy(_.size)) atau dengan huruf pertama ( groupBy(_.head)).
ohruunuruus

2
Kekurangannya adalah banyak koleksi yang tidak berguna (karena hanya ukuran yang dibutuhkan) yang dibuat.
Yann Moisan

bagaimana jika saya ingin mendefinisikan peta akumulator dalam ekspresi itu daripada membuat peta baru?
Tobias Kolb

128

koleksi scala memang memiliki count:list.count(_ == 2)


48

Saya memiliki masalah yang sama dengan Sharath Prabhal, dan saya mendapatkan solusi lain (bagi saya yang lebih jelas):

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

Dengan hasil:

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

44
Versi yang lebih bersih adalahs.groupBy(identity).mapValues(_.size)
ohruunuruus

1
@ohruunuruus ini seharusnya menjadi jawaban (vs komentar); saya akan senang untuk antusias upvote, jika itu (dan pilih sebagai jawaban terbaik jika saya adalah OP);
doug

1
@doug agak baru untuk SO dan tidak yakin, tapi senang menurut
ohruunuruus

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

memberi

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

Perhatikan bahwa Anda dapat mengganti (i=>i)dengan identityfungsi bawaan:

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

menyukai solusi singkat menggunakan pustaka
bawaan

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
tetapi menghasilkan angka tersebut. kemunculan untuk setiap nilai sebanyak nilai muncul — tampaknya tidak efisien dan tidak terlalu berguna ...
Erik Kaplun

13

Memulai Scala 2.13, metode groupMapReduce melakukannya sekaligus melalui daftar:

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

Ini:

  • groups daftar elemen (bagian grup dari grup MapReduce)

  • maps setiap kejadian nilai yang dikelompokkan ke 1 (bagian peta dari Map Reduce)

  • reducenilai s dalam grup nilai ( _ + _) dengan menjumlahkannya (kurangi bagian dari groupMap Reduce ).

Ini adalah versi sekali jalan dari apa yang dapat diterjemahkan oleh:

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

Bagus, inilah yang saya cari, saya merasa sedih bahwa bahkan aliran Java (yang tidak bagus dalam beberapa aspek) memungkinkan ini dalam satu kali lintasan sementara Scala tidak bisa.
Dici

9

Saya mengalami masalah yang sama tetapi ingin menghitung beberapa item sekaligus ..

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


mungkin menggunakan Streamdan jawaban yang diterima akan menghasilkan tujuan Anda "sekali jalan" ditambah kode yang lebih jelas.
juanchito

Solusi ini mengulang Daftar hanya satu kali, menggunakan groupBy dan kemudian map akan melakukannya dua kali.
ruloweb

7

Jika Anda ingin menggunakannya seperti list.count(2)Anda harus mengimplementasikannya menggunakan Kelas Implisit .

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

Jawaban singkat:

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

Jawaban panjang:

Menggunakan Scalaz , diberikan.

import scalaz._, Scalaz._

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

lalu semua ini (dalam urutan dari yang kurang disederhanakan menjadi lebih disederhanakan)

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

menghasilkan

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

6

Menarik untuk dicatat bahwa peta dengan nilai default 0, sengaja dirancang untuk kasus ini menunjukkan kinerja terburuk (dan tidak sesingkat 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]) {
    }

menghasilkan

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

Sangat mengherankan bahwa paling ringkas groupBylebih cepat daripada peta yang bisa berubah!


3
Saya agak curiga dengan benchmark ini karena tidak jelas ukuran datanya. The groupBysolusi melakukan toLowertapi yang lain tidak. Juga mengapa menggunakan pola yang cocok untuk peta - gunakan saja mapValues. Jadi gabungkan itu dan Anda mendapatkan def woGrouped(w: Word): Map[Char, Int] = w.groupBy(identity).mapValues(_.size)- mencobanya dan periksa kinerja untuk berbagai daftar ukuran. Akhirnya di solusi lain, mengapa a) mendeklarasikan mapdan b) menjadikannya var ?? Lakukan sajaw.foldLeft(Map.empty[Char, Int])...
samthebest

1
Terima kasih telah memberikan lebih banyak data (mengubah pilihan saya :). Saya pikir alasan mengapa implementasi groupBy menggunakan peta yang bisa berubah Builderyang dioptimalkan untuk peningkatan berulang. Ini kemudian mengubah peta yang bisa berubah menjadi yang tidak bisa diubah menggunakan MapBuilder. Mungkin ada beberapa evaluasi malas yang terjadi di bawah tenda juga untuk membuat segalanya lebih cepat.
Samthebest

@samthebest Anda cukup mencari penghitung dan menaikkannya. Saya tidak melihat apa yang bisa disimpan di cache di sana. Cache harus berupa peta dengan jenis yang sama.
Val

Saya tidak mengatakan itu menyimpan apa pun. Saya membayangkan peningkatan kinerja berasal dari penggunaan Builders, dan mungkin beberapa evaluasi malas.
samthebest

@samthebest lazy evaluation = evaluasi tertunda (dipanggil menurut nama) + caching. Anda tidak dapat berbicara tentang evaluasi malas tetapi tidak menyimpan cache.
Val

4

Saya tidak mendapatkan ukuran daftar yang digunakan lengthmelainkan sizeseperti yang disarankan oleh jawaban di atas karena masalah yang dilaporkan di sini .

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

3

Ini opsi lain:

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

menggunakan kucing

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 iterasi melalui urutan aslinya! Bahkan seq.groupBy(identity).mapValues(_.size)hanya melalui dua kali.
WeaponsGrade

Jumlah iterasi mungkin tidak peduli untuk string kecil seperti "Alphabet", tetapi ketika berhadapan dengan jutaan item dalam koleksi, iterasi tentu melakukan hal!
WeaponsGrade

2

Coba ini, harus berhasil.


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

Ini akan mengembalikan 3


Bagaimana ini berbeda dari jawaban xiefei yang diberikan tujuh tahun lalu?
jwvh

0

Berikut ini cara yang cukup mudah untuk melakukannya.

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)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.