Wie zähle ich bestimmte Elemente im Array?

160

Ich habe ein Array:

[1, 2, 3, 5, 2, 8, 9, 2]

Ich würde gerne wissen, wie viele 2s im Array sind.

Was ist die eleganteste Art, dies in JavaScript zu tun, ohne eine Schleife mit einer forSchleife zu erstellen?

Leem
quelle

Antworten:

90

Sehr einfach:

var count = 0;
for(var i = 0; i < array.length; ++i){
    if(array[i] == 2)
        count++;
}
Thor Jacobsen
quelle
52
Nein, was ich meine, ist ohne Schleife mit "für"
Leem
9
@Leem: Warum ist Schleifen schlecht? Irgendwann gibt es immer eine Schleife. Natürlich würden Sie eine Funktion erstellen, die die Schleife verbirgt. Diese "Ich möchte nicht das richtige Werkzeug für den Job verwenden" -Anfragen machten für mich nie viel Sinn. Und wir können darüber streiten, was am elegantesten ist. Zum Beispiel ist es für mich nicht elegant, einen Funktionsaufruf pro Element durchzuführen, um ihn nur mit einem Wert zu vergleichen.
Felix Kling
2
zum Lachen: alert (eval ('(' + my_array.join ('== 2) + (') + '== 2)')) jsfiddle.net/gaby_de_wilde/gujbmych
user40521
29
Das OP hält Schleifen wahrscheinlich für schlecht, da es sich um 5 Codezeilen handelt und einen veränderlichen Status erfordert. Ein Entwickler, der das später liest, muss einige Zeit damit verbringen, zu überprüfen, was es tut, und den Fokus von seiner Aufgabe verlieren. Eine Abstraktion ist weit überlegen: const count = countItems(array, 2);und die Implementierungsdetails können im Inneren argumentiert werden.
Joeytwiddle
2
Dies ist nicht die richtige Antwort, da die Frage eindeutig verlangt, keine Schleifen zu verwenden. Überprüfen Sie meine Lösung, die keine Schleifen verwendet. stackoverflow.com/a/44743436/8211014
Luis Orantes
287

[ Diese Antwort ist etwas veraltet: Lesen Sie die Änderungen ]

Sag Hallo zu deinen Freunden: mapund filterund reduceund forEachundevery usw.

(Ich schreibe nur gelegentlich for-Schleifen in Javascript, da das Scoping auf Blockebene fehlt. Sie müssen daher ohnehin eine Funktion als Hauptteil der Schleife verwenden, wenn Sie Ihren Iterationsindex oder -wert erfassen oder klonen müssen. For-Schleifen sind im Allgemeinen effizienter, aber manchmal brauchen Sie einen Verschluss.)

Der am besten lesbare Weg:

[....].filter(x => x==2).length

(Wir hätten schreiben können .filter(function(x){return x==2}).length stattdessen )

Das Folgende ist platzsparender (O (1) als O (N)), aber ich bin mir nicht sicher, wie viel Zeit / Strafe Sie in Bezug auf die Zeit zahlen könnten (nicht mehr als ein konstanter Faktor seit Ihrem Besuch jedes Element genau einmal):

[....].reduce((total,x) => (x==2 ? total+1 : total), 0)

(Wenn Sie diesen bestimmten Code optimieren müssen, ist eine for-Schleife in einigen Browsern möglicherweise schneller. Sie können die Dinge auf jsperf.com testen.)


Sie können dann elegant sein und daraus einen Prototyp machen:

[1, 2, 3, 5, 2, 8, 9, 2].count(2)

So was:

Object.defineProperties(Array.prototype, {
    count: {
        value: function(value) {
            return this.filter(x => x==value).length;
        }
    }
});

Sie können auch die reguläre alte For-Loop-Technik (siehe andere Antworten) in die obige Eigenschaftsdefinition einfügen (auch dies wäre wahrscheinlich viel schneller).


2017 bearbeiten :

Hoppla, diese Antwort ist populärer geworden als die richtige. Verwenden Sie einfach die akzeptierte Antwort. Während diese Antwort niedlich sein mag, optimieren die js-Compiler solche Fälle wahrscheinlich nicht (oder können es aufgrund der Spezifikation nicht). Sie sollten also wirklich eine einfache for-Schleife schreiben:

Object.defineProperties(Array.prototype, {
    count: {
        value: function(query) {
            /* 
               Counts number of occurrences of query in array, an integer >= 0 
               Uses the javascript == notion of equality.
            */
            var count = 0;
            for(let i=0; i<this.length; i++)
                if (this[i]==query)
                    count++;
            return count;
        }
    }
});

Sie könnten eine Version definieren, .countStrictEq(...)die den ===Begriff der Gleichheit verwendet. Der Begriff der Gleichheit kann für das, was Sie tun, wichtig sein! (Zum Beispiel [1,10,3,'10'].count(10)==2, weil Zahlen wie '4' == 4 in Javascript ... daher wird es genannt .countEqoder .countNonstrictbetont, dass es das verwendet== Operator .)

Erwägen Sie auch die Verwendung Ihrer eigenen Multiset-Datenstruktur (z. B. Python ' collections.Counter'), um zu vermeiden, dass Sie zuerst zählen müssen.

class Multiset extends Map {
    constructor(...args) {
        super(...args);
    }
    add(elem) {
        if (!this.has(elem))
            this.set(elem, 1);
        else
            this.set(elem, this.get(elem)+1);
    }
    remove(elem) {
        var count = this.has(elem) ? this.get(elem) : 0;
        if (count>1) {
            this.set(elem, count-1);
        } else if (count==1) {
            this.delete(elem);
        } else if (count==0)
            throw `tried to remove element ${elem} of type ${typeof elem} from Multiset, but does not exist in Multiset (count is 0 and cannot go negative)`;
            // alternatively do nothing {}
    }
}

Demo:

> counts = new Multiset([['a',1],['b',3]])
Map(2) {"a" => 1, "b" => 3}

> counts.add('c')
> counts
Map(3) {"a" => 1, "b" => 3, "c" => 1}

> counts.remove('a')
> counts
Map(2) {"b" => 3, "c" => 1}

> counts.remove('a')
Uncaught tried to remove element a of type string from Multiset, but does not exist in Multiset (count is 0 and cannot go negative)

Nebenbemerkung: Wenn Sie jedoch immer noch die Möglichkeit der funktionalen Programmierung (oder einen wegwerfbaren Einzeiler ohne Überschreiben von Array.prototype) wollten, könnten Sie ihn heutzutage knapper schreiben als [...].filter(x => x==2).length. Wenn Sie sich für die Leistung interessieren, beachten Sie, dass dies zwar asymptotisch die gleiche Leistung wie die for-Schleife (O (N) -Zeit) ist, jedoch möglicherweise O (N) zusätzlichen Speicher (anstelle von O (1) -Speicher) benötigt, da dies fast der Fall ist Erzeugen Sie auf jeden Fall ein Zwischenarray und zählen Sie dann die Elemente dieses Zwischenarrays.

Ninjagecko
quelle
1
Dies ist eine gute FP-Lösung, das einzige "Problem" (für die meisten Fälle irrelevant), das ein Zwischenarray erzeugt.
Tokland
1
@tokland: Wenn das ein Problem ist, können Sie tunarray.reduce(function(total,x){return x==value? : total+1 : total}, 0)
Ninjagecko
1
@ninjagecko Sollte der ternäre Operator nicht nur einen Doppelpunkt enthalten? [...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
A.Krueger
2
@tokland Möglicherweise erstellt der Filter kein Zwischenarray. Ein guter optimierender Compiler kann leicht erkennen, dass nur die Länge des Arrays verwendet wird. Vielleicht ist keiner der aktuellen JS-Compiler klug genug, dies zu tun, aber das ist nicht wichtig. Wie Fowler sagt: "Wenn etwas weh tut, dann mach mehr davon". Es ist kurzsichtig, Compiler-Mängel durch das Schreiben von schlechtem Code zu vermeiden. Wenn der Compiler nicht funktioniert, beheben Sie den Compiler. mlafeldt.github.io/blog/if-it-hurts-do-it-more-often
John Henckel
Ich würde dies als optimale Lösung für 2017 betrachten : const count = (list) => list.filter((x) => x == 2).length. Verwenden Sie es dann, indem Sie aufrufen, count(list)wobei list ein Array von Zahlen ist. Sie können auch const count = (list) => list.filter((x) => x.someProp === 'crazyValue').lengthInstanzen von crazyValue im Array von Objekten zählen. Beachten Sie, dass es eine genaue Übereinstimmung für die Eigenschaft ist.
agm1984
69

ES6-Update auf JS:

Beachten Sie, dass Sie immer Triple Equals verwenden sollten: ===um einen korrekten Vergleich zu erhalten:

// Let has local scope
let array = [1, 2, 3, 5, 2, 8, 9, 2]

// Functional filter with an Arrow function
array.filter(x => x === 2).length  // -> 3

Die folgende einstimmige Pfeilfunktion (Lambda-Funktion) in JS:

(x) => {
   const k = 2
   return k * x
}

kann zu dieser prägnanten Form für eine einzelne Eingabe vereinfacht werden:

x => 2 * x

wo das returnimpliziert ist.

Sverrisson
quelle
Ist die Filterfunktion leistungsfähiger als die Verwendung der es6 for of-Schleife?
Niklas
1
@Niklas, ich denke, es ist das gleiche (da beide alle Elemente überprüfen müssen, O (N)), aber ich denke, es ist browserabhängig und auch von der Anzahl der Elemente und dem Computer, was in den Cache-Speicher passt. Also, ich denke die Antwort ist: "Es ist komplex" :)
Sverrisson
67

2017: Wenn sich noch jemand für die Frage interessiert, lautet meine Lösung wie folgt:

const arrayToCount = [1, 2, 3, 5, 2, 8, 9, 2];
const result = arrayToCount.filter(i => i === 2).length;
console.log('number of the found elements: ' + result);

Raild
quelle
8

Wenn Sie lodash verwenden oder einen Unterstrich verwenden, stellt die Methode _.countBy ein Objekt mit Gesamtsummen bereit, die von jedem Wert im Array eingegeben werden. Sie können dies in einen Einzeiler verwandeln, wenn Sie nur einen Wert zählen müssen:

_.countBy(['foo', 'foo', 'bar'])['foo']; // 2

Dies funktioniert auch gut bei Arrays von Zahlen. Der Einzeiler für Ihr Beispiel wäre:

_.countBy([1, 2, 3, 5, 2, 8, 9, 2])[2]; // 3
Coleman
quelle
5
Riesiger Overkill. Da es Zähler für alle einzigartigen Elemente erstellt. Verschwendete Lagerung und Zeit.
Metalim
5

Der seltsamste Weg, den ich mir vorstellen kann, ist:

(a.length-(' '+a.join(' ')+' ').split(' '+n+' ').join(' ').match(/ /g).length)+1

Wo:

  • a ist das Array
  • n ist die Zahl, die im Array gezählt werden soll

Mein Vorschlag, benutze eine Weile oder eine Schleife ;-)

Gary Green
quelle
3

Wenn Sie keine Schleife verwenden, müssen Sie den Prozess normalerweise an eine Methode übergeben, die dies tut eine Schleife verwendet.

Hier ist eine Möglichkeit, wie unser Loop-Hass-Codierer seinen Ekel zu einem Preis befriedigen kann:

var a=[1, 2, 3, 5, 2, 8, 9, 2];

alert(String(a).replace(/[^2]+/g,'').length);


/*  returned value: (Number)
3
*/

Sie können indexOf auch wiederholt aufrufen, wenn es als Array-Methode verfügbar ist, und den Suchzeiger jedes Mal verschieben.

Dadurch wird kein neues Array erstellt, und die Schleife ist schneller als ein forEach oder Filter.

Es könnte einen Unterschied machen, wenn Sie eine Million Mitglieder zum Anschauen haben.

function countItems(arr, what){
    var count= 0, i;
    while((i= arr.indexOf(what, i))!= -1){
        ++count;
        ++i;
    }
    return count
}

countItems(a,2)

/*  returned value: (Number)
3
*/
kennebec
quelle
2
Sie könnten Ihre Regex auf nur reduzieren - beachten Sie jedoch, dass String(a).match(/2/g).length + 1dies oder Ihre Implementierung nicht mit zweistelligen Zahlen gut funktioniert.
Gary Green
2
wie wäre es mit [2, 22, 2]?
Oduvan
2

Die meisten veröffentlichten Lösungen, die Array-Funktionen wie Filter verwenden, sind unvollständig, da sie nicht parametrisiert sind.

Hier ist eine Lösung, mit der das zu zählende Element zur Laufzeit eingestellt werden kann.

function elementsCount(elementToFind, total, number){
    return total += number==elementToFind;
}

var ar = [1, 2, 3, 5, 2, 8, 9, 2];
var elementToFind=2;
var result = ar.reduce(elementsCount.bind(this, elementToFind), 0);

Der Vorteil dieses Ansatzes besteht darin, dass die Funktion leicht geändert werden kann, um beispielsweise die Anzahl der Elemente größer als X zu zählen.

Sie können die Reduktionsfunktion auch inline deklarieren

var ar = [1, 2, 3, 5, 2, 8, 9, 2];
var elementToFind=2;
var result = ar.reduce(function (elementToFind, total, number){
    return total += number==elementToFind;
}.bind(this, elementToFind), 0);
Luis Orantes
quelle
var elementToFind=2; ... function (elementToFind, total, number){ return total += number==elementToFind; }.bind(this, elementToFind) ...ist schwerer zu lesen und bietet keinen Vorteil gegenüber nur ... (acc, x) => acc += number == 2.... Ich mag deine Verwendung von +=anstatt acc + (number == 2)obwohl. Fühlt sich allerdings wie ein ungerechtfertigter Syntax-HACK an.
Masterxilo
2

Wirklich, warum brauchst du mapoder filterdafür? reducewurde für diese Art von Operationen "geboren":

[1, 2, 3, 5, 2, 8, 9, 2].reduce( (count,2)=>count+(item==val), 0);

das ist es! (Wenn item==valin jeder Iteration, wird 1 zum Akkumulator hinzugefügt count, wie in aufgelöst truewird 1).

Als eine Funktion:

function countInArray(arr, val) {
   return arr.reduce((count,item)=>count+(item==val),0)
}

Oder erweitern Sie Ihre Arrays:

Array.prototype.count = function(val) {
   return this.reduce((count,item)=>count+(item==val),0)
}
Yuval A.
quelle
2

Es ist besser, es in Funktion zu verpacken:

let countNumber = (array,specificNumber) => {
    return array.filter(n => n == specificNumber).length
}

countNumber([1,2,3,4,5],3) // returns 1
Justin Herrera
quelle
2

Hier ist eine ES2017 + -Methode, um die Anzahl aller Array-Elemente in O (N) abzurufen:

const arr = [1, 2, 3, 5, 2, 8, 9, 2];
const counts = {};

arr.forEach((el) => {
  counts[el] = counts[el] ? (counts[el] += 1) : 1;
});

Optional können Sie die Ausgabe auch sortieren:

const countsSorted = Object.entries(counts).sort(([_, a], [__, b]) => a - b);

console.log (countSorted) für Ihr Beispielarray:

[
  [ '2', 3 ],
  [ '1', 1 ],
  [ '3', 1 ],
  [ '5', 1 ],
  [ '8', 1 ],
  [ '9', 1 ]
]
Yanick J. Steinbeck
quelle
1

Ich glaube, was Sie suchen, ist ein funktionaler Ansatz

    const arr = ['a', 'a', 'b', 'g', 'a', 'e'];
    const count = arr.filter(elem => elem === 'a').length;
    console.log(count); // Prints 3

elem === 'a' ist die Bedingung, ersetzen Sie es durch Ihre eigene.

Shantanu Bhadoria
quelle
Es wird nicht 3, sondern 0 gedruckt. Um dies zu beheben, sollte Ihre zweite Zeile count = arr.filter(elem => elem === 'a').lengthodercount = arr.filter(elem => {return elem === 'a'}).length
WPomier
1

Ich bin ein Anfänger der Reduzierungsfunktion von js array.

const myArray =[1, 2, 3, 5, 2, 8, 9, 2];
const count = myArray.reduce((count, num) => num === 2 ? count + 1 : count, 0)

Wenn Sie wirklich Lust haben, können Sie eine Zählfunktion für den Array-Prototyp erstellen. Dann können Sie es wiederverwenden.

Array.prototype.count = function(filterMethod) {
  return this.reduce((count, item) => filterMethod(item)? count + 1 : count, 0);
} 

Dann mach

const myArray =[1, 2, 3, 5, 2, 8, 9, 2]
const count = myArray.count(x => x==2)
Scott Blanch
quelle
0

Lösung durch Rekursion

function count(arr, value) {
   if (arr.length === 1)    {
      return arr[0] === value ? 1 : 0;
   } else {
      return (arr.shift() === value ? 1 : 0) + count(arr, value);
   }
}

count([1,2,2,3,4,5,2], 2); // 3
Giffo
quelle
1
Behandelt dies ein leeres Array?
Andrew Grimm
@ AndrewGrimm hat es richtig. Basisfall ist arr.length == 0
Justin Meiners
Schöne Lösung! Ich habe versucht, etwas mit Rekursion nur zum Üben zu tun, und Ihr Beispiel war eleganter als das, was ich tat. Es ist definitiv eine komplexere Methode als die Verwendung oder eine einfache und auch teurere Methode filter, wenn es um die Leistung geht, aber dennoch eine großartige Möglichkeit, dies mit Rekursion zu tun. Meine einzige Änderung ist: Ich denke, es wäre besser, eine Funktion zu erstellen und einen Filter hinzuzufügen, um das Array zu kopieren und eine Mutation des ursprünglichen Arrays zu vermeiden. Verwenden Sie dann die rekursive Funktion als innere Funktion. reduceforLoop
R. Marques
0

var arrayCount = [1,2,3,2,5,6,2,8];
var co = 0;
function findElement(){
    arrayCount.find(function(value, index) {
      if(value == 2)
        co++;
    });
    console.log( 'found' + ' ' + co + ' element with value 2');
}

Ich würde so etwas tun:

var arrayCount = [1,2,3,4,5,6,7,8];

function countarr(){
  var dd = 0;
  arrayCount.forEach( function(s){
    dd++;
  });

  console.log(dd);
}

Roni
quelle
0

Erstellen Sie eine neue Methode für die Array-Klasse in einer Datei auf Kernebene und verwenden Sie sie im gesamten Projekt.

// say in app.js
Array.prototype.occurrence = function(val) {
  return this.filter(e => e === val).length;
}

Verwenden Sie dies überall in Ihrem Projekt -

[1, 2, 4, 5, 2, 7, 2, 9].occurrence(2);
// above line returns 3
Rushikesh Bharad
quelle
0

Hier ist ein Einzeiler in Javascript.

  1. Karte verwenden. Finden Sie die passenden Werte(v === 2) im Array und geben Sie ein Array mit Einsen und Nullen zurück.
  2. Verwenden Sie Reduzieren. Fügen Sie alle Werte des Arrays für die gefundene Gesamtzahl hinzu.
[1, 2, 3, 5, 2, 8, 9, 2]
  .map(function(v) {
    return v === 2 ? 1 : 0;
  })
  .reduce((a, b) => a + b, 0);

Das Ergebnis ist 3.

Jake
quelle
0

Abhängig davon, wie Sie es ausführen möchten:

const reduced = (array, val) => { // self explanatory
    return array.filter((element) => element === val).length;
}

console.log(reduced([1, 2, 3, 5, 2, 8, 9, 2], 2));

// 3

const reducer = (array) => { // array to set > set.forEach > map.set
    const count = new Map();
    const values = new Set(array);
    values.forEach((element)=> {
        count.set(element, array.filter((arrayElement) => arrayElement === element).length);
    });
    return count;
}
console.log(reducer([1, 2, 3, 5, 2, 8, 9, 2]));

// Map(6) {1 => 1, 2 => 3, 3 => 1, 5 => 1, 8 => 1, …}
Mohammad Hassan
quelle
-7

Sie können die Eigenschaft length im JavaScript-Array verwenden:

var myarray = [];
var count = myarray.length;//return 0

myarray = [1,2];
count = myarray.length;//return 2
Bhavik Bhavsar
quelle
Wenn Sie es zuerst filtern, können Sie die Länge verwenden. dh array.filter (x => x === 2) .length
Scott Blanch