Ich weiß nicht, ob es einen richtigen Weg gibt, um die Größe einer Liste in Scala zu ermitteln, aber für Ihre Situation könnten Sie eine Sequenz verwenden.
Qusay Fantazia
Ist diese Frage noch unbeantwortet? Fragen, weil Sie vielleicht vergessen haben, eine zu akzeptieren.
Tobias Kolb
Antworten:
150
Eine etwas sauberere Version einer der anderen Antworten lautet:
val s =Seq("apple","oranges","apple","banana","apple","oranges","oranges")
s.groupBy(identity).mapValues(_.size)
Geben Sie ein Mapmit einer Zählung für jedes Element in der ursprünglichen Sequenz:
Map(banana ->1, oranges ->3, apple ->3)
In der Frage wird gefragt, wie die Anzahl eines bestimmten Elements ermittelt werden kann. Bei diesem Ansatz würde die Lösung das Zuordnen des gewünschten Elements zu seinem Zählwert wie folgt erfordern:
Es ist die Identitätsfunktion, wie hier diskutiert . Die Funktion groupByerfordert eine Funktion, die auf Elemente angewendet wird, damit sie diese gruppieren kann. Eine Alternative zum Gruppieren der Zeichenfolgen in der Antwort nach ihrer Identität könnte beispielsweise das Gruppieren nach ihrer Länge ( groupBy(_.size)) oder nach ihrem ersten Buchstaben ( groupBy(_.head)) sein.
Ohruunuruus
2
Der Nachteil ist, dass viele nutzlose Sammlungen (weil nur die Größe benötigt wird) erstellt werden.
Yann Moisan
Was wäre, wenn ich in diesem Ausdruck eine Akkumulator-Map definieren wollte, anstatt eine neue Map zu erstellen?
Eine etwas sauberere Version ists.groupBy(identity).mapValues(_.size)
ohruunuruus
1
@ohruunuruus dies sollte eine Antwort sein (vs Kommentar); Ich würde gerne begeistert abstimmen, wenn es so wäre (und es als die beste Antwort auswählen, wenn ich das OP wäre);
Doug
1
@ Doug etwas neu in SO und war nicht sicher, aber glücklich zu verpflichten
ohruunuruus
27
list.groupBy(i=>i).mapValues(_.size)
gibt
Map[Int,Int]=Map(1->1,2->3,7->1,3->1,4->3)
Beachten Sie, dass Sie durch (i=>i)integrierte identityFunktion ersetzen können:
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))
Schön, das ist, wonach ich gesucht habe. Ich fand es traurig, dass selbst Java-Streams (die in einigen Aspekten nicht gut sind) dies in einem einzigen Durchgang zulassen, während Scala dies nicht konnte.
Dici
8
Ich hatte das gleiche Problem, wollte aber mehrere Elemente auf einmal zählen.
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)
Es ist interessant festzustellen, dass die Karte mit dem Standardwert 0, die absichtlich für diesen Fall entwickelt wurde, die schlechteste Leistung aufweist (und nicht so präzise wie groupBy).
Ich bin etwas misstrauisch gegenüber diesem Benchmark, da nicht klar ist, wie groß die Daten sind. Die groupByLösung führt eine aus toLower, die anderen jedoch nicht. Warum auch eine Musterübereinstimmung für die Karte verwenden? Verwenden Sie einfach mapValues. Also rollen Sie das zusammen und Sie erhalten def woGrouped(w: Word): Map[Char, Int] = w.groupBy(identity).mapValues(_.size)- versuchen Sie das und überprüfen Sie die Leistung für verschiedene Größenlisten. Schließlich in den anderen Lösungen, warum a) deklarieren mapund b) machen es eine var? Tun w.foldLeft(Map.empty[Char, Int])...
Sie es
1
Vielen Dank für die Bereitstellung weiterer Daten (meine Stimme wurde geändert :). Ich denke, der Grund dafür ist, dass die Implementierung von groupBy eine veränderbare Map von Builders verwendet, die für iterative Inkremente optimiert sind. Anschließend konvertiert es die veränderbare Karte mit a in eine unveränderliche MapBuilder. Es gibt wahrscheinlich auch einige faule Bewertungen unter der Haube, um die Dinge schneller zu machen.
Samthebest
@samthebest Sie suchen einfach den Zähler und erhöhen ihn. Ich sehe nicht, was dort zwischengespeichert werden kann. Der Cache muss sowieso eine Karte der gleichen Art sein.
Val
Ich sage nicht, dass es irgendetwas zwischenspeichert. Ich stelle mir vor, dass die Leistungssteigerung durch die Verwendung von Builders und möglicherweise durch eine verzögerte Bewertung verursacht wird.
Samthebest
@samthebest faule Auswertung = verzögerte Auswertung (Aufruf mit Namen) + Caching. Sie können nicht über verzögerte Auswertung sprechen, aber nicht über Caching.
Val
4
Ich habe die Größe der Liste nicht mit erhalten length, sondern sizeals eine, die die obige Antwort aufgrund des hier gemeldeten Problems vorschlug .
val list =List("apple","oranges","apple","banana","apple","oranges","oranges")
list.groupBy(x=>x).map(t =>(t._1, t._2.size))
Wow, 4 Iterationen durch die ursprüngliche Sequenz! Auch seq.groupBy(identity).mapValues(_.size)geht nur zweimal durch.
WeaponsGrade
Die Anzahl der Iterationen spielt für eine kleine Zeichenfolge wie "Alphabet" möglicherweise keine Rolle, aber wenn es um Millionen von Elementen in einer Sammlung geht, spielen Iterationen sicherlich eine Rolle!
WeaponsGrade
2
Versuchen Sie dies, sollte funktionieren.
val list =List(1,2,4,2,4,7,3,2,4)
list.count(_==2)
Wie unterscheidet sich das von der Antwort von xiefei vor sieben Jahren?
JWVH
0
Hier ist ein ziemlich einfacher Weg, dies zu tun.
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)
Antworten:
Eine etwas sauberere Version einer der anderen Antworten lautet:
Geben Sie ein
Map
mit einer Zählung für jedes Element in der ursprünglichen Sequenz:In der Frage wird gefragt, wie die Anzahl eines bestimmten Elements ermittelt werden kann. Bei diesem Ansatz würde die Lösung das Zuordnen des gewünschten Elements zu seinem Zählwert wie folgt erfordern:
quelle
groupBy
erfordert eine Funktion, die auf Elemente angewendet wird, damit sie diese gruppieren kann. Eine Alternative zum Gruppieren der Zeichenfolgen in der Antwort nach ihrer Identität könnte beispielsweise das Gruppieren nach ihrer Länge (groupBy(_.size)
) oder nach ihrem ersten Buchstaben (groupBy(_.head)
) sein.Scala-Sammlungen haben
count
:list.count(_ == 2)
quelle
Ich hatte das gleiche Problem wie Sharath Prabhal und bekam eine andere (für mich klarere) Lösung:
Mit als Ergebnis:
quelle
s.groupBy(identity).mapValues(_.size)
gibt
Beachten Sie, dass Sie durch
(i=>i)
integrierteidentity
Funktion ersetzen können:quelle
quelle
Ab
Scala 2.13
der groupMapReduce tut Verfahren , dass in einem Durchlauf durch die Liste:Dies:
group
s Listenelemente (Gruppenteil der Gruppe MapReduce)map
s jeweils gruppiert Wert Auftreten bis 1 (Teil der Gruppen Karte Map Reduce)reduce
s Werte innerhalb einer Gruppe von Werten (_ + _
) durch Summieren (reduzieren Sie einen Teil von groupMap Reduce ).Dies ist eine One-Pass-Version dessen, was übersetzt werden kann durch:
quelle
Ich hatte das gleiche Problem, wollte aber mehrere Elemente auf einmal zählen.
https://gist.github.com/sharathprabhal/6890475
quelle
Stream
ergibt die Verwendung und die akzeptierte Antwort Ihr Ziel "One Go" plus klareren Code.Wenn Sie es so verwenden möchten, müssen
list.count(2)
Sie es mit einer impliziten Klasse implementieren .quelle
Kurze Antwort:
Lange Antwort:
Mit Scalaz gegeben.
dann alle diese (in der Reihenfolge von weniger vereinfacht bis mehr vereinfacht)
Ausbeute
quelle
Es ist interessant festzustellen, dass die Karte mit dem Standardwert 0, die absichtlich für diesen Fall entwickelt wurde, die schlechteste Leistung aufweist (und nicht so präzise wie
groupBy
).produziert
Es ist merkwürdig, dass die meisten prägnanten
groupBy
schneller sind als selbst veränderbare Karten!quelle
groupBy
Lösung führt eine austoLower
, die anderen jedoch nicht. Warum auch eine Musterübereinstimmung für die Karte verwenden? Verwenden Sie einfachmapValues
. Also rollen Sie das zusammen und Sie erhaltendef woGrouped(w: Word): Map[Char, Int] = w.groupBy(identity).mapValues(_.size)
- versuchen Sie das und überprüfen Sie die Leistung für verschiedene Größenlisten. Schließlich in den anderen Lösungen, warum a) deklarierenmap
und b) machen es eine var? Tunw.foldLeft(Map.empty[Char, Int])...
Builder
s verwendet, die für iterative Inkremente optimiert sind. Anschließend konvertiert es die veränderbare Karte mit a in eine unveränderlicheMapBuilder
. Es gibt wahrscheinlich auch einige faule Bewertungen unter der Haube, um die Dinge schneller zu machen.Builder
s und möglicherweise durch eine verzögerte Bewertung verursacht wird.Ich habe die Größe der Liste nicht mit erhalten
length
, sondernsize
als eine, die die obige Antwort aufgrund des hier gemeldeten Problems vorschlug .quelle
Hier ist eine weitere Option:
quelle
quelle
mit Katzen
quelle
seq.groupBy(identity).mapValues(_.size)
geht nur zweimal durch.Versuchen Sie dies, sollte funktionieren.
Es wird 3 zurückgeben
quelle
Hier ist ein ziemlich einfacher Weg, dies zu tun.
quelle