Scala bir listedeki olayların sayısını nasıl hesaplayabilirim


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

Bunu şu şekilde uygulamak istiyorum: list.count(2)(3 döndürür).


Ölçekli bir liste boyutu elde etmenin uygun bir yolu var mı bilmiyorum, ama sizin durumunuz için bir sıralama kullanabilirsiniz.
Kusay Fantazia

Bu soru hala cevaplanmamış mı? Sormak çünkü birini kabul etmeyi unutmuş olabilirsiniz.
Tobias Kolb

Yanıtlar:


152

Diğer cevaplardan birinin biraz daha net bir versiyonu:

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

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

Maporijinal sıradaki her öğe için bir sayı vererek :

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

Soru, belirli bir öğenin sayısının nasıl bulunacağını sorar. Bu yaklaşımla çözüm, istenen öğenin sayı değerine aşağıdaki gibi eşlenmesini gerektirecektir:

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

3
"kimlik" nedir?
Igorock

5
Tartışıldığı gibi, kimlik fonksiyonudur burada . İşlev groupBy, öğelere uyguladığı bir işlevi gerektirir, böylece onları nasıl gruplandıracağını bilir. Yanıttaki dizeleri kimliklerine göre gruplamaya bir alternatif, örneğin uzunluklarına ( groupBy(_.size)) veya ilk harflerine ( groupBy(_.head)) göre gruplama olabilir .
ohruunuruus

2
Bunun dezavantajı, pek çok yararsız koleksiyonun (çünkü yalnızca boyuta ihtiyaç duyulduğu için) oluşturulmasıdır.
Yann Moisan

yeni bir harita oluşturmak yerine bu ifadede bir akümülatör haritası tanımlamak istersem ne olur?
Tobias Kolb

129

scala koleksiyonlarında şunlar bulunur count:list.count(_ == 2)


48

Sharath Prabhal ile aynı problemi yaşadım ve başka (benim için daha net) bir çözüm buldum:

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

Sonuç olarak:

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

45
Biraz daha temiz bir versiyons.groupBy(identity).mapValues(_.size)
ohruunuruus

1
@ohruunuruus bu bir cevap olmalıdır (yorum vs); eğer öyleyse, coşkuyla oy vermeyi çok isterim (ve OP olsaydım bunu en iyi cevap olarak seçerim);
doug

1
@doug SO'da biraz yeni ve emin değildim, ama buna mecbur olmaktan mutluyum
ohruunuruus

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

verir

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

Değiştirmek unutmayın (i=>i)yerleşik identityfonksiyonu:

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

yerleşik kitaplıkları kullanarak kısa çözümleri seviyorum
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
ama num verir. her değer için oluşumlar, değer ortaya
çıktıkça

14

Başlangıç ​​olarak Scala 2.13, groupMapReduce yöntemi bunu listeden bir geçişte yapar:

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

Bu:

  • groups listesi elemanlarının (grup parçası grubu MapReduce)

  • maps her gruplanmış değer oluşumu 1'e ( Harita Azaltma grubunun harita kısmı )

  • reduceBir değer grubu içindeki değerleri ( _ + _) toplayarak (groupMap Azalt'ın bir kısmını azaltın ).

Bu, neyin çevrilebileceğinin tek geçişli bir sürümüdür :

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

Güzel, aradığım şey buydu, Scala yapamıyorken Java akışlarının bile (bazı yönlerden iyi olmayan) buna tek geçişte izin vermesi üzücü buldum.
Dici

9

Aynı problemle karşılaştım ancak tek seferde birden fazla öğeyi saymak istedim ..

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


belki kullanmak Streamve kabul edilen cevabı "tek seferde" hedefiniz artı daha net bir kod verecektir.
juanchito

Bu çözüm Listeyi groupBy kullanarak yalnızca bir kez yineler ve ardından map bunu iki kez yapar.
ruloweb

7

Eğer onu kullanmak istiyorsanız Örtük Sınıflist.count(2) kullanarak uygulamak zorundasınız .

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

Kısa cevap:

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

Uzun cevap:

Scalaz kullanarak , verilen.

import scalaz._, Scalaz._

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

sonra tüm bunlar (daha az sadeleştirmeden daha sadeleştirmeye doğru)

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

Yol ver

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

6

Bu durum için kasıtlı olarak tasarlanan varsayılan 0 değerine sahip haritanın en kötü performansı gösterdiğini (ve o kadar kısa olmadığını groupBy) not etmek ilginçtir.

    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]) {
    }

üretir

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

Çoğu özlü groupByolanın değiştirilebilir haritadan bile daha hızlı olması ilginçtir !


3
Verinin boyutunun ne olduğu net olmadığı için bu kıyaslama konusunda biraz şüpheliyim. groupBySolüsyon bir gerçekleştirir toLowerama diğerleri yok. Ayrıca neden harita için bir kalıp eşleşmesi kullanın - sadece kullanın mapValues. Bunu bir araya getirin ve elde edin def woGrouped(w: Word): Map[Char, Int] = w.groupBy(identity).mapValues(_.size)- bunu deneyin ve çeşitli boyut listeleri için performansı kontrol edin. Son olarak diğer çözümlerde, neden a) bildirmek mapve b) onu bir değişken yapmak ?? Sadece yapw.foldLeft(Map.empty[Char, Int])...
samthebest

1
Daha fazla veri sağladığınız için teşekkürler (oyumu değiştirdim :). Bence groupBy uygulamasının Builderyinelemeli artışlar için optimize edilmiş değişken bir s haritası kullanmasının nedeni . Daha sonra, değiştirilebilir haritayı bir MapBuilder. Muhtemelen işleri daha hızlı hale getirmek için bazı tembel değerlendirmeler de var.
samthebest

@samthebest Sadece sayacı arayın ve artırın. Orada nelerin önbelleğe alınabileceğini göremiyorum. Zaten önbelleğin aynı türden bir harita olması gerekir.
Val

Hiçbir şeyi sakladığını söylemiyorum. Performans artışının Builders kullanımından ve muhtemelen bazı tembel değerlendirmelerden geldiğini düşünüyorum .
samthebest

@samthebest tembel değerlendirme = gecikmiş değerlendirme (adıyla arama) + önbelleğe alma. Tembel değerlendirme hakkında konuşamazsınız, ancak önbelleğe alma hakkında konuşamazsınız.
Val

4

Kullanarak listenin boyutunu almadım, lengthbunun yerine sizeyukarıdaki yanıtın burada bildirilen sorun nedeniyle önerdiği gibi .

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

3

İşte başka bir seçenek:

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

kedileri kullanmak

import cats.implicits._

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

2
Vay canına, orijinal dizide 4 yineleme! Hatta seq.groupBy(identity).mapValues(_.size)sadece iki kez geçiyor.
WeaponsGrade

Tekrarlamalar sayısı bir koleksiyon öğeleri milyonlarca uğraşırken önemli değil "Alfabe" gibi küçük bir dize için, ancak, yinelemeleri kesinlikle may yapmak olsun!
SilahlarGrade

2

Bunu dene, çalışmalı.


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

3 dönecek


1
Bu, xiefei'nin yedi yıl önce verdiği yanıttan nasıl farklıdır?
jwvh

0

İşte bunu yapmanın oldukça kolay bir yolu.

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.