Scala làm cách nào để tôi có thể đếm số lần xuất hiện trong danh sách


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

Tôi muốn triển khai nó như thế này: list.count(2)(trả về 3).


Tôi không biết có cách nào thích hợp để lấy kích thước của danh sách theo tỷ lệ hay không, nhưng đối với trường hợp của bạn, bạn có thể sử dụng một chuỗi.
Qusay Fantazia

Câu hỏi này vẫn chưa được trả lời? Hỏi vì có thể bạn đã quên chấp nhận.
Tobias Kolb

Câu trả lời:


150

Một phiên bản gọn gàng hơn của một trong những câu trả lời khác là:

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

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

đưa ra một Mapsố lượng cho mỗi mục trong chuỗi ban đầu:

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

Câu hỏi hỏi làm thế nào để tìm số lượng của một mặt hàng cụ thể. Với cách tiếp cận này, giải pháp sẽ yêu cầu ánh xạ phần tử mong muốn với giá trị đếm của nó như sau:

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

2
"bản sắc" là gì?
Igorock

4
Nó là chức năng nhận dạng, như đã thảo luận ở đây . Hàm groupByyêu cầu một hàm mà nó áp dụng cho các phần tử để nó biết cách nhóm chúng. Một cách thay thế để nhóm các chuỗi trong câu trả lời theo danh tính của chúng có thể là nhóm theo độ dài của chúng ( groupBy(_.size)) hoặc theo chữ cái đầu tiên của chúng ( groupBy(_.head)).
ohruunuruus

2
Hạn chế là rất nhiều bộ sưu tập vô dụng (vì chỉ cần kích thước) được tạo ra.
Yann Moisan

Điều gì sẽ xảy ra nếu tôi muốn xác định một bản đồ tích lũy trong biểu thức đó thay vì tạo một bản đồ mới?
Tobias Kolb

128

bộ sưu tập scala có count:list.count(_ == 2)


48

Tôi đã gặp vấn đề tương tự như Sharath Prabhal và tôi có một giải pháp khác (với tôi rõ ràng hơn):

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

Kết quả là:

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

44
Một phiên bản hơi sạch hơn làs.groupBy(identity).mapValues(_.size)
ohruunuruus

1
@ohruunuruus đây phải là một câu trả lời (so với bình luận); tôi rất muốn nhiệt tình ủng hộ, nếu có (và chọn nó là câu trả lời hay nhất nếu tôi là OP);
doug

1
@doug hơi mới đối với SO và không chắc chắn, nhưng rất vui khi được bắt buộc
ohruunuruus

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

cho

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

Lưu ý rằng bạn có thể thay thế (i=>i)bằng identityhàm tích hợp:

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

tình yêu giải pháp ngắn bằng cách sử dụng built-in thư viện
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
nhưng nó tạo ra số. xuất hiện cho mỗi giá trị bao nhiêu lần tùy giá trị xảy ra-dường như không hiệu quả và không phải là rất hữu ích ...
Erik Kaplun

13

Bắt đầu Scala 2.13, phương thức groupMapReduce thực hiện điều đó trong một lần chuyển qua danh sách:

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

Điều này:

  • groups các phần tử danh sách (một phần nhóm của nhóm MapReduce)

  • maps mỗi lần xuất hiện giá trị được nhóm thành 1 (phần bản đồ của nhóm Map Reduce)

  • reduces các giá trị trong một nhóm giá trị ( _ + _) bằng cách cộng chúng (giảm một phần của groupMap Reduce ).

Đây là phiên bản một lần của những gì có thể được dịch bởi:

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

Thật tuyệt, đây là những gì tôi đang tìm kiếm, tôi thấy thật buồn khi ngay cả các luồng Java (không tốt ở một số khía cạnh) cho phép điều này trong một lần vượt qua trong khi Scala không thể.
Dici

8

Tôi gặp phải vấn đề tương tự nhưng muốn đếm nhiều mục trong một lần ..

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


có lẽ việc sử dụng Streamvà câu trả lời được chấp nhận sẽ mang lại mục tiêu của bạn là "một lượt" cộng với mã rõ ràng hơn.
juanchito

Giải pháp này chỉ lặp lại Danh sách một lần, sử dụng groupBy và sau đó bản đồ sẽ thực hiện điều đó hai lần.
ruloweb,

7

Nếu bạn muốn sử dụng nó giống như list.count(2)bạn phải triển khai nó bằng một Lớp ngầm định .

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

Câu trả lời ngắn:

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

Câu trả lời dài:

Sử dụng Scalaz , đã cho.

import scalaz._, Scalaz._

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

sau đó tất cả những thứ này (theo thứ tự từ ít đơn giản hơn đến đơn giản hơn)

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

năng suất

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

6

Điều thú vị là bản đồ có giá trị 0 mặc định, được thiết kế có chủ ý cho trường hợp này thể hiện hiệu suất kém nhất (và không ngắn gọn bằng 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]) {
    }

sản xuất

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

Thật tò mò rằng ngắn gọn nhất groupBycòn nhanh hơn cả bản đồ có thể thay đổi!


3
Tôi hơi nghi ngờ về điểm chuẩn này vì không rõ kích thước của dữ liệu. Các groupBygiải pháp thực hiện một toLowernhưng những người khác thì không. Ngoài ra, tại sao lại sử dụng kết hợp mẫu cho bản đồ - chỉ cần sử dụng mapValues. Vì vậy, cuộn nó lại với nhau và bạn sẽ có được def woGrouped(w: Word): Map[Char, Int] = w.groupBy(identity).mapValues(_.size)- hãy thử và kiểm tra hiệu suất cho các danh sách kích thước khác nhau. Cuối cùng trong các giải pháp khác, tại sao a) khai báo mapvà b) biến nó thành var ?? Just dow.foldLeft(Map.empty[Char, Int])...
samthebest

1
Cảm ơn đã cung cấp thêm dữ liệu (đã thay đổi phiếu bầu của tôi :). Tôi nghĩ lý do tại sao việc triển khai groupBy lại sử dụng một bản đồ có thể thay đổi của Builders được tối ưu hóa cho các bước tăng lặp lại. Sau đó, nó chuyển đổi bản đồ có thể thay đổi thành bất biến bằng cách sử dụng a MapBuilder. Có lẽ cũng có một số đánh giá lười biếng đang diễn ra để làm cho mọi thứ nhanh hơn.
samthebest

@samthebest Bạn chỉ cần tra cứu bộ đếm và tăng nó lên. Tôi không thấy những gì có thể được lưu trữ ở đó. Bộ nhớ cache dù sao cũng cần phải là một bản đồ cùng loại.
Val

Tôi không nói nó lưu trữ bất cứ thứ gì. Tôi tưởng tượng sự gia tăng hiệu suất đến từ việc sử dụng Builders và có thể là một số đánh giá lười biếng.
samthebest

@samthebest lười đánh giá = đánh giá chậm trễ (gọi tên) + bộ nhớ đệm. Bạn không thể nói về đánh giá lười biếng nhưng không phải bộ nhớ đệm.
Val

4

Tôi không nhận được kích thước của danh sách bằng cách sử dụng lengthsizelà một trong những câu trả lời ở trên đề xuất nó vì sự cố được báo cáo ở đây .

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

3

Đây là một tùy chọn khác:

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

sử dụng mèo

import cats.implicits._

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

2
Chà, 4 lần lặp qua trình tự ban đầu! Thậm chí seq.groupBy(identity).mapValues(_.size)chỉ trải qua hai lần.
WeaponsGrade 28/09/18

Số lần lặp có thể không quan trọng đối với một chuỗi nhỏ như "Bảng chữ cái", nhưng khi xử lý hàng triệu mục trong một bộ sưu tập, số lần lặp chắc chắn rất quan trọng!
WeaponsGrade

2

Hãy thử điều này, sẽ hoạt động.


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

Nó sẽ trả về 3


Câu trả lời này khác với câu trả lời của xiefei bảy năm trước như thế nào?
jwvh

0

Đây là một cách khá dễ dàng để làm điều đó.

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.