Scala ฉันจะนับจำนวนครั้งที่เกิดขึ้นในรายการได้อย่างไร


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

ฉันต้องการใช้มันดังนี้: list.count(2)(ส่งกลับ 3)


ฉันไม่รู้ว่ามีวิธีที่เหมาะสมในการรับขนาดของรายการในสกาล่าหรือไม่ แต่สำหรับสถานการณ์ของคุณคุณสามารถใช้ลำดับได้
Qusay Fantazia

คำถามนี้ยังไม่มีคำตอบหรือไม่? ถามเพราะคุณอาจลืมที่จะยอมรับ
Tobias Kolb

คำตอบ:


152

คำตอบอื่น ๆ ที่ค่อนข้างสะอาดกว่าคือ:

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

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

การให้การMapนับสำหรับแต่ละรายการในลำดับเดิม:

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

คำถามจะถามว่าจะหาจำนวนรายการเฉพาะได้อย่างไร ด้วยวิธีการนี้การแก้ปัญหาจะต้องมีการแมปองค์ประกอบที่ต้องการกับค่าการนับดังนี้:

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

3
"ตัวตน" คืออะไร?
Igorock

5
มันเป็นฟังก์ชั่นตัวตนตามที่กล่าวไว้ที่นี่ ฟังก์ชันgroupByต้องการฟังก์ชันที่ใช้กับองค์ประกอบเพื่อให้รู้วิธีจัดกลุ่ม อีกทางเลือกหนึ่งในการจัดกลุ่มสตริงในคำตอบตามอัตลักษณ์อาจเป็นพูดจัดกลุ่มตามความยาว ( groupBy(_.size)) หรือตามตัวอักษรตัวแรก ( groupBy(_.head))
ohruunuruus

2
ข้อเสียคือมีการสร้างคอลเลกชันที่ไร้ประโยชน์จำนวนมาก (เพราะจำเป็นต้องใช้ขนาดเท่านั้น)
Yann Moisan

จะเกิดอะไรขึ้นถ้าฉันต้องการกำหนดแผนที่สะสมในนิพจน์นั้นแทนที่จะสร้างแผนที่ใหม่
Tobias Kolb


48

ฉันมีปัญหาเดียวกันกับ Sharath Prabhal และฉันมีวิธีแก้ปัญหาอื่น (สำหรับฉันที่ชัดเจนกว่า):

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

ด้วยผลลัพธ์:

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

45
เวอร์ชันที่ค่อนข้างสะอาดกว่าคือs.groupBy(identity).mapValues(_.size)
ohruunuruus

1
@ohruunuruus นี้ควรจะเป็นคำตอบ (เทียบกับความคิดเห็น); ฉันชอบที่จะโหวตเพิ่มอย่างกระตือรือร้นถ้าเป็น (และเลือกเป็นคำตอบที่ดีที่สุดถ้าฉันเป็น OP)
doug

1
@doug ค่อนข้างใหม่สำหรับ SO และไม่แน่ใจ แต่ยินดีที่จะบังคับ
ohruunuruus

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

ให้

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

โปรดทราบว่าคุณสามารถแทนที่(i=>i)ด้วยidentityฟังก์ชันในตัว:

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

ชอบวิธีแก้ปัญหาสั้น ๆ โดยใช้ไลบรารีในตัว
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
แต่ให้จำนวน การเกิดขึ้นสำหรับแต่ละค่าหลาย ๆ ครั้งเมื่อค่าเกิดขึ้นดูเหมือนไม่มีประสิทธิภาพและไม่มีประโยชน์มากนัก ...
Erik Kaplun

14

เริ่มต้นScala 2.13ที่groupMapReduceวิธีไม่ว่าในหนึ่งผ่านรายการ:

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

นี้:

  • groupองค์ประกอบรายการ (ส่วนกลุ่มของกลุ่ม MapReduce)

  • mapค่าที่จัดกลุ่มแต่ละค่าจะเกิดขึ้นเป็น 1 (ส่วนแผนที่ของการลดแผนที่กลุ่ม)

  • reduces ค่าภายในกลุ่มของค่า ( _ + _) โดยการสรุป (ลดส่วนหนึ่งของ groupMap ลด )

นี่เป็นเวอร์ชันเดียวที่สามารถแปลได้โดย:

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

ดีนี่คือสิ่งที่ฉันกำลังมองหาฉันพบว่ามันน่าเศร้าที่แม้แต่สตรีม Java (ซึ่งไม่ดีในบางแง่มุม) ก็อนุญาตให้ใช้สิ่งนี้ในการส่งครั้งเดียวในขณะที่ Scala ทำไม่ได้
Dici

9

ฉันพบปัญหาเดียวกัน แต่ต้องการนับหลายรายการในครั้งเดียว ..

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


บางทีการใช้Streamและคำตอบที่ยอมรับจะทำให้คุณบรรลุเป้าหมาย "ครั้งเดียว" พร้อมรหัสที่ชัดเจน
juanchito

โซลูชันนี้จะวนซ้ำรายการเพียงครั้งเดียวโดยใช้ groupBy จากนั้นแผนที่จะทำซ้ำสองครั้ง
ruloweb

7

หากคุณต้องการที่จะใช้มันเหมือนlist.count(2)คุณต้องใช้มันโดยใช้ชั้นโดยปริยาย

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

คำตอบสั้น ๆ :

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

คำตอบยาว:

ใช้Scalazให้

import scalaz._, Scalaz._

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

จากนั้นทั้งหมดเหล่านี้ (ตามลำดับความง่ายน้อยลงเพื่อให้ง่ายขึ้น)

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

ผลผลิต

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

6

เป็นที่น่าสังเกตว่าแผนที่ที่มีค่าเริ่มต้น 0 ซึ่งออกแบบมาโดยเจตนาสำหรับกรณีนี้แสดงให้เห็นถึงประสิทธิภาพที่แย่ที่สุด (และไม่รัดกุมเท่า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]) {
    }

ผลิต

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

เป็นที่น่าแปลกใจที่ความกระชับส่วนใหญ่groupByเร็วกว่าแผนที่ที่ไม่แน่นอนด้วยซ้ำ!


3
ฉันสงสัยเล็กน้อยเกี่ยวกับเกณฑ์มาตรฐานนี้เนื่องจากไม่ชัดเจนว่าขนาดของข้อมูลคือเท่าใด groupByแก้ปัญหาการดำเนินการtoLowerแต่คนอื่นทำไม่ได้ นอกจากนี้ยังมีเหตุผลที่ใช้การแข่งขันแบบแผนที่ - mapValuesเพียงแค่ใช้ ดังนั้นม้วนเข้าด้วยกันและคุณจะได้รับdef woGrouped(w: Word): Map[Char, Int] = w.groupBy(identity).mapValues(_.size)- ลองและตรวจสอบประสิทธิภาพสำหรับรายการขนาดต่างๆ สุดท้ายในการแก้ปัญหาอื่นทำไม a) ประกาศmapและ b) ทำให้เป็น var ?? แค่ทำw.foldLeft(Map.empty[Char, Int])...
samthebest

1
ขอบคุณที่ให้ข้อมูลเพิ่มเติม (เปลี่ยนการโหวตของฉัน :) ฉันคิดว่าสาเหตุที่การใช้งาน groupBy ใช้แผนที่ที่ไม่แน่นอนของBuilders ซึ่งได้รับการปรับให้เหมาะสมสำหรับการเพิ่มการวนซ้ำ จากนั้นจะแปลงแผนที่ที่ไม่เปลี่ยนแปลงเป็นไม่เปลี่ยนรูปโดยใช้ไฟล์MapBuilder. อาจมีการประเมินที่ขี้เกียจเกิดขึ้นภายใต้ประทุนด้วยเพื่อให้สิ่งต่างๆเร็วขึ้น
samthebest

@samthebest คุณเพียงแค่ค้นหาเคาน์เตอร์และเพิ่มมัน ฉันไม่เห็นสิ่งที่สามารถแคชได้ที่นั่น แคชต้องเป็นแผนที่ประเภทเดียวกันอยู่ดี
วัล

ฉันไม่ได้บอกว่ามันแคชอะไร ฉันคิดว่าประสิทธิภาพที่เพิ่มขึ้นมาจากการใช้Builders และอาจเป็นการประเมินที่ขี้เกียจ
samthebest

@samthebest lazy evaluation = การประเมินล่าช้า (เรียกตามชื่อ) + การแคช คุณไม่สามารถพูดเกี่ยวกับการประเมินแบบขี้เกียจ แต่ไม่ได้แคช
วาล

4

ฉันไม่ได้รับขนาดของรายการใช้lengthแต่sizeเป็นหนึ่งในคำตอบข้างต้นปัญหาเพราะปัญหาที่มีการรายงานที่นี่

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

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

ใช้แมว

import cats.implicits._

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

2
ว้าว 4 การทำซ้ำตามลำดับดั้งเดิม! แม้seq.groupBy(identity).mapValues(_.size)จะผ่านไปเพียงสองครั้ง
WeaponsGrade

จำนวนซ้ำอาจจะไม่ได้เป็นเรื่องสำหรับสตริงขนาดเล็กเช่น "ตัวอักษร" แต่เมื่อต้องรับมือกับล้านรายการในคอลเลกชัน, การทำซ้ำอย่างแน่นอนทำเรื่อง!
WeaponsGrade

2

ลองนี้น่าจะได้ผล


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

มันจะกลับมา 3


1
สิ่งนี้แตกต่างจากคำตอบของ xiefei เมื่อเจ็ดปีก่อนอย่างไร
jwvh

0

นี่เป็นวิธีที่ง่ายมากในการทำ

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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.