Wie kann die Anzahl der Schlüssel / Eigenschaften eines Objekts in JavaScript effizient gezählt werden?


1544

Was ist der schnellste Weg, um die Anzahl der Schlüssel / Eigenschaften eines Objekts zu zählen? Ist es möglich, dies zu tun, ohne über das Objekt zu iterieren? dh ohne zu tun

var count = 0;
for (k in myobj) if (myobj.hasOwnProperty(k)) count++;

(Firefox hat eine magische __count__Eigenschaft bereitgestellt , die jedoch irgendwo um Version 4 entfernt wurde.)



2
ein Leistungsbenchmark für verschiedene Arten: jsben.ch/#/oSt2p
EscapeNetscape

1
Mögliches Duplikat der Länge eines JavaScript-Objekts
Adam Katz

Antworten:


2516

So tun Sie dies in jeder ES5-kompatiblen Umgebung wie Node , Chrome, IE 9+, Firefox 4+ oder Safari 5+:

Object.keys(obj).length

8
Nicht nur Node.js, sondern jede Umgebung, die ES5 unterstützt
Yi Jiang

59
Übrigens ... habe gerade einige Tests ausgeführt ... diese Methode wird in O (n) Zeit ausgeführt. Eine for-Schleife ist nicht viel schlechter als diese Methode. ** trauriges Gesicht ** stackoverflow.com/questions/7956554/…
BMiner

161
-1 (-200, wenn ich könnte) Dies durchläuft nicht nur das Objekt, sondern erstellt auch ein ganz neues Array mit all seinen Schlüsseln, sodass die Beantwortung der Frage nicht vollständig möglich ist.
GetFree

42
Es scheint viel schneller zu sein als das für (zumindest auf Chrome 25): jsperf.com/count-elements-in-object
fserb

34
@ GetFree Warum so viele Daumen hoch? Dies ist definitiv der schnellste Weg in Bezug auf die Codierung. Keine zusätzlichen Methoden oder Bibliotheken erforderlich. In Bezug auf die Codegeschwindigkeit ist es anscheinend auch nicht schlecht. Überhaupt kein vollständiger Fehler. 87 Daumen hoch scheitert für Sie.
Andrew

150

Sie könnten diesen Code verwenden:

if (!Object.keys) {
    Object.keys = function (obj) {
        var keys = [],
            k;
        for (k in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, k)) {
                keys.push(k);
            }
        }
        return keys;
    };
}

Dann können Sie dies auch in älteren Browsern verwenden:

var len = Object.keys(obj).length;

2
Was ist der Zweck der Prüfung (Object.prototype.hasOwnProperty.call(obj, k))?
Styfle

14
@styfle Wenn Sie eine for- Schleife verwenden, um die Eigenschaften des Objekts zu durchlaufen , erhalten Sie auch die Eigenschaften in der Prototypenkette. Deshalb ist eine Überprüfung hasOwnPropertyerforderlich. Es werden nur Eigenschaften zurückgegeben, die für das Objekt selbst festgelegt wurden.
Renaat De Muynck

13
@styfle Um es einfacher zu machen, kannst du einfach schreiben obj.hasOwnProperty(k)(ich habe das tatsächlich in meinem ursprünglichen Beitrag gemacht, aber später aktualisiert). hasOwnPropertyist für jedes Objekt verfügbar, da es Teil des ObjectPrototyps des Objekts ist. In dem seltenen Fall, dass diese Methode entfernt oder überschrieben wird, erhalten Sie möglicherweise unerwartete Ergebnisse. Wenn Sie Object.prototypees von dort aus aufrufen, wird es etwas robuster. Der Grund für die Verwendung callliegt darin, dass Sie die Methode objanstelle des Prototyps aufrufen möchten .
Renaat De Muynck

6
Wäre es nicht besser, diese Version zu verwenden? developer.mozilla.org/en-US/docs/JavaScript/Reference/…
Xavier Delamotte

1
@ XavierDelamotte Sie sind absolut richtig. Während meine Version funktioniert, ist sie sehr einfach und dient als Beispiel. Mozillas Code ist sicherer. (PS: Ihr Link ist auch in der akzeptierten Antwort)
Renaat De Muynck

137

Wenn Sie Underscore.js verwenden , können Sie _.size verwenden (danke @douwe):
_.size(obj)

Alternativ können Sie auch _.keys verwenden, die für einige möglicherweise klarer sind:
_.keys(obj).length

Ich kann Underscore nur empfehlen, es ist eine enge Bibliothek für viele grundlegende Dinge. Wann immer möglich, stimmen sie mit ECMA5 überein und verschieben sich auf die native Implementierung.

Ansonsten unterstütze ich die Antwort von @ Avi. Ich habe es bearbeitet, um einen Link zum MDC-Dokument hinzuzufügen, der die keys () -Methode enthält, die Sie Nicht-ECMA5-Browsern hinzufügen können.


9
Wenn Sie underscore.js verwenden, sollten Sie stattdessen _.size verwenden. Das Gute ist, dass das Ergebnis gleich bleibt, wenn Sie irgendwie vom Array zum Objekt oder umgekehrt wechseln.
Douwe

2
Und nach meinem Verständnis ist lodash im Allgemeinen besser als Unterstrich (obwohl sie ähnliche Dinge tun).
Merlyn Morgan-Graham

1
@ MerlynMorgan-Graham, wenn ich mich richtig erinnere, ist lodash ursprünglich eine Gabel des Unterstrichs ...
molson504x

6
_.keys(obj).lengthhat für mich am besten funktioniert, da mein Rückgabeobjekt manchmal eine einfache Zeichenfolge ohne Eigenschaften ist. _.size(obj)gibt mir die Länge der Zeichenfolge zurück, während _.keys(obj).length0 zurückgibt.
Jacob Stamm

O (n) Komplexität . Lodash und Underscore werden Object.keysintern verwendet. Der Unterstrich kopiert auch jeden Schlüssel in ein Array innerhalb einer for..inSchleife, wenn er Object.keysnicht definiert ist.
N. Kudryavtsev

82

Die Standardobjektimplementierung ( ES5.1 Objektinterne Eigenschaften und Methoden ) erfordert keine ObjectVerfolgung der Anzahl der Schlüssel / Eigenschaften. Daher sollte es keine Standardmethode geben, um die Größe einer zu bestimmen, Objectohne explizit oder implizit über ihre Schlüssel zu iterieren.

Hier sind die am häufigsten verwendeten Alternativen:

1. Object.keys () von ECMAScript

Object.keys(obj).length;Funktioniert durch internes Durchlaufen der Schlüssel, um ein temporäres Array zu berechnen und dessen Länge zurückzugeben.

  • Vorteile - Lesbare und saubere Syntax. Keine Bibliothek oder benutzerdefinierter Code erforderlich, außer einem Shim, wenn keine native Unterstützung verfügbar ist
  • Nachteile - Speicheraufwand aufgrund der Erstellung des Arrays.

2. Bibliotheksbasierte Lösungen

Viele bibliotheksbasierte Beispiele an anderer Stelle in diesem Thema sind nützliche Redewendungen im Kontext ihrer Bibliothek. Unter dem Gesichtspunkt der Leistung gibt es jedoch nichts zu gewinnen im Vergleich zu einem perfekten Code ohne Bibliothek, da alle diese Bibliotheksmethoden entweder eine for-Schleife oder ES5 Object.keys(nativ oder shimmed) enthalten.

3. Optimieren einer for-Schleife

Der langsamste Teil einer solchen for-Schleife ist .hasOwnProperty()aufgrund des Funktionsaufrufaufwands im Allgemeinen der Aufruf. Wenn ich also nur die Anzahl der Einträge eines JSON-Objekts möchte, überspringe ich den .hasOwnProperty()Aufruf, wenn ich weiß, dass kein Code erweitert wurde oder wird Object.prototype.

Andernfalls könnte Ihr Code geringfügig optimiert werden, indem Sie klocal ( var k) erstellen und ++countanstelle von postfix den Präfix-Inkrement-Operator ( ) verwenden.

var count = 0;
for (var k in myobj) if (myobj.hasOwnProperty(k)) ++count;

Eine andere Idee beruht auf dem Zwischenspeichern der hasOwnPropertyMethode:

var hasOwn = Object.prototype.hasOwnProperty;
var count = 0;
for (var k in myobj) if (hasOwn.call(myobj, k)) ++count;

Ob dies in einer bestimmten Umgebung schneller ist oder nicht, ist eine Frage des Benchmarking. Es ist ohnehin ein sehr begrenzter Leistungsgewinn zu erwarten.


Warum sollte var k in myobjdie Leistung gesteigert werden? Soweit ich weiß, deklarieren nur Funktionen neuen Bereich in JavaScript. Sind In-Loops eine Ausnahme von dieser Regel?
Lars Gyrup Brink Nielsen

1
Ist das schneller for (var k in myobj) hasOwn.call(myobj, k) && ++count;dh die if-Anweisung durch ein einfaches && ersetzen?
Hamza Kubba

Das Letzte, was Sie tun können : Object.getOwnPropertyNames(obj).length; viel einfacher.
Wilt

29

Wenn Sie tatsächlich auf ein Leistungsproblem stoßen, würde ich empfehlen, die Aufrufe zum Hinzufügen / Entfernen von Eigenschaften zum / vom Objekt mit einer Funktion zu versehen, die auch eine Eigenschaft mit entsprechendem Namen (Größe?) Inkrementiert / dekrementiert.

Sie müssen die anfängliche Anzahl der Eigenschaften nur einmal berechnen und von dort aus fortfahren. Wenn es kein tatsächliches Leistungsproblem gibt, stören Sie sich nicht. Wickeln Sie einfach diesen Code in eine Funktion ein getNumberOfProperties(object)und fertig.


4
@hitautodestruct Weil er eine Lösung anbietet.
Crush

@crush Diese Antwort scheint eher Vorschläge zu machen, als eine direkte Lösung zu finden.
Hitautodestruct

5
@hitautodestruct schlägt eine Antwort vor: Inkrementieren / Dekrementieren einer gekapselten Anzahl mit den Add / Remove-Methoden. Es gibt eine andere Antwort genau so unten. Der einzige Unterschied ist, dass Confusion keinen Code angeboten hat. Antworten müssen nicht nur Codelösungen bereitstellen.
Crush

1
es mag nicht perfekt sein ... aber verglichen mit den anderen "Antworten" kann dies die beste Lösung für einige Situationen sein
d.raev

1
Bisher ist dies die einzige Lösung, die ich sehe, nämlich O (1) konstante zeitliche Leistungskomplexität, und daher die einzige Lösung, die das Fragendetail von "ohne Iteration" beantwortet und daher die wirklich akzeptierte Antwort sein sollte. Die meisten, wenn nicht alle anderen Antworten beantworten dies nicht, da sie eine O (n) lineare Zeitleistungskomplexität bieten. Dies gilt auch für 1-zeilige Lösungen, die so etwas wie eine .keys () -Funktion aufrufen, da solche Funktionsaufrufe O (n) sind.
Cellepo

17

Wie von Avi Flax angegeben https://stackoverflow.com/a/4889658/1047014

Object.keys(obj).length

führt den Trick für alle aufzählbaren Eigenschaften Ihres Objekts aus, aber um auch die nicht aufzählbaren Eigenschaften einzuschließen, können Sie stattdessen die verwenden Object.getOwnPropertyNames. Hier ist der Unterschied:

var myObject = new Object();

Object.defineProperty(myObject, "nonEnumerableProp", {
  enumerable: false
});
Object.defineProperty(myObject, "enumerableProp", {
  enumerable: true
});

console.log(Object.getOwnPropertyNames(myObject).length); //outputs 2
console.log(Object.keys(myObject).length); //outputs 1

console.log(myObject.hasOwnProperty("nonEnumerableProp")); //outputs true
console.log(myObject.hasOwnProperty("enumerableProp")); //outputs true

console.log("nonEnumerableProp" in myObject); //outputs true
console.log("enumerableProp" in myObject); //outputs true

Wie hier angegeben, hat dies die gleiche Browserunterstützung wieObject.keys

In den meisten Fällen möchten Sie die nicht aufzählbaren Werte möglicherweise nicht in diese Art von Operationen einbeziehen, aber es ist immer gut, den Unterschied zu kennen;)


1
Daumen hoch zum Erwähnen Object.getOwnPropertyNames, du warst der einzige hier ...
Wilt

15

Mir ist keine Möglichkeit bekannt, dies zu tun. Um jedoch die Iterationen auf ein Minimum zu beschränken, können Sie versuchen, die Existenz von zu überprüfen. __count__Wenn diese nicht vorhanden sind (dh nicht Firefox), können Sie das Objekt durchlaufen und definieren es für die spätere Verwendung zB:

if (myobj.__count__ === undefined) {
  myobj.__count__ = ...
}

Auf diese Weise würde jeder Browser, der dies unterstützt, __count__dies verwenden, und Iterationen würden nur für diejenigen ausgeführt, die dies nicht tun. Wenn sich die Anzahl ändert und Sie dies nicht tun können, können Sie es jederzeit zu einer Funktion machen:

if (myobj.__count__ === undefined) {
  myobj.__count__ = function() { return ... }
  myobj.__count__.toString = function() { return this(); }
}

Auf diese Weise können Sie jederzeit auf myobj verweisen. __count__Die Funktion wird ausgelöst und neu berechnet.


13
Beachten Sie, dass Object.prototype.__count__in Gecko 1.9.3 entfernt wird: whereswalden.com/2010/04/06/... Zählung -property-of-Objekte-wird-wird-entfernt /
dshaw

17
Nachdem Firefox 4 veröffentlicht wurde, ist diese Antwort veraltet. Object.__count__ist weg, und auch gute Befreiung.
Yi Jiang

1
Ich würde nicht sagen, dass die Antwort veraltet ist. Es ist immer noch eine interessante Strategie, einen Wert in eine Funktion zu kapseln.
devios1

sollte das Prototypobjekt zum Erweitern verwenden
Aaria Carter-Weir

13

Um mit Avi Flax zu iterieren, antworten Sie auf Object.keys (obj) .length ist für ein Objekt korrekt, an das keine Funktionen gebunden sind

Beispiel:

obj = {"lol": "what", owo: "pfft"};
Object.keys(obj).length; // should be 2

gegen

arr = [];
obj = {"lol": "what", owo: "pfft"};
obj.omg = function(){
    _.each(obj, function(a){
        arr.push(a);
    });
};
Object.keys(obj).length; // should be 3 because it looks like this 
/* obj === {"lol": "what", owo: "pfft", omg: function(){_.each(obj, function(a){arr.push(a);});}} */

Schritte, um dies zu vermeiden:

  1. Fügen Sie keine Funktionen in ein Objekt ein, in dem Sie die Anzahl der Schlüssel zählen möchten

  2. Verwenden Sie ein separates Objekt oder erstellen Sie ein neues Objekt speziell für Funktionen (wenn Sie zählen möchten, wie viele Funktionen in der Datei enthalten sind Object.keys(obj).length)

auch ja ich habe das _ oder unterstreichen modul von nodejs in meinem beispiel verwendet

Dokumentation finden Sie hier http://underscorejs.org/ sowie die Quelle auf Github und verschiedene andere Informationen

Und schließlich eine lodash-Implementierung https://lodash.com/docs#size

_.size(obj)


Als Antwort auf Ihre Kommentare zu Array(obj).length: Es funktioniert nicht. http://jsfiddle.net/Jhy8M/
Jamie Chong

Ja, ich habe ein bisschen mehr darüber nachgedacht. Ich werde diese Antwort nach Möglichkeit entfernen oder einfach alles zusammen bearbeiten
Belldandu

Ich sehe nicht, dass dies etwas mit Funktionen zu tun hat, und in Chrome sehe ich dieses Verhalten überhaupt nicht. Ich würde vermuten , dass dies gehabt haben konnte , mit dem Standardverhalten zu tun Object.defineProperty (): enumerable das ist false, aber ich habe noch keine Dokumentation gefunden , wie var obj = { a: true, b: true }von dem unterscheiden kann var obj = {}; obj.a = true; obj.b = true;oder einfach , wenn eine andere Interpretation / Semantik der W3 wurde von Chrome übernommen.
Nolo

10

wie oben beantwortet: Object.keys(obj).length

Aber: Da wir jetzt eine echte Map- Klasse in ES6 haben, würde ich vorschlagen , sie anstelle der Eigenschaften eines Objekts zu verwenden.

const map = new Map();
map.set("key", "value");
map.size; // THE fastest way

8

Für diejenigen, die Underscore.js in ihrem Projekt haben, können Sie Folgendes tun:

_({a:'', b:''}).size() // => 2

oder funktionaler Stil:

_.size({a:'', b:''}) // => 2

8

Hier sind einige Leistungstests für drei Methoden;

https://jsperf.com/get-the-number-of-keys-in-an-object

Object.keys (). Länge

20.735 Operationen pro Sekunde

Sehr einfach und kompatibel. Läuft schnell, aber teuer, weil es ein neues Array von Schlüsseln erstellt, die dann weggeworfen werden.

return Object.keys(objectToRead).length;

Schleife durch die Tasten

15.734 Operationen pro Sekunde

let size=0;
for(let k in objectToRead) {
  size++
}
return size;

Etwas langsamer, aber bei weitem nicht in der Nähe der Speichernutzung. Daher wahrscheinlich besser, wenn Sie an einer Optimierung für mobile oder andere kleine Maschinen interessiert sind

Karte anstelle von Objekt verwenden

953.839.338 Operationen pro Sekunde

return mapToRead.size;

Grundsätzlich verfolgt Map seine eigene Größe, sodass wir nur ein Zahlenfeld zurückgeben. Weit, weit schneller als jede andere Methode. Wenn Sie die Kontrolle über das Objekt haben, konvertieren Sie sie stattdessen in Karten.


6

Von: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Object.defineProperty (obj, prop, deskriptor)

Sie können es entweder allen Ihren Objekten hinzufügen:

Object.defineProperty(Object.prototype, "length", {
    enumerable: false,
    get: function() {
        return Object.keys(this).length;
    }
});

Oder ein einzelnes Objekt:

var myObj = {};
Object.defineProperty(myObj, "length", {
    enumerable: false,
    get: function() {
        return Object.keys(this).length;
    }
});

Beispiel:

var myObj = {};
myObj.name  = "John Doe";
myObj.email = "leaked@example.com";
myObj.length; //output: 2

Auf diese Weise hinzugefügt, wird es nicht in for..in-Schleifen angezeigt:

for(var i in myObj) {
     console.log(i + ":" + myObj[i]);
}

Ausgabe:

name:John Doe
email:leaked@example.com

Hinweis: Es funktioniert nicht in <IE9-Browsern.


Wenn Sie integrierte Prototypen erweitern oder eine Eigenschaft (z. B. Affen-Patch) mehrfach ausfüllen möchten, gehen Sie bitte korrekt vor: Überprüfen Sie aus Gründen der Vorwärtskompatibilität, ob die Eigenschaft zuerst vorhanden ist, und machen Sie die Eigenschaft dann nicht aufzählbar, sodass die eigenen Schlüssel vorhanden sind von konstruierten Objekten werden nicht verschmutzt. Verwenden Sie für Methoden tatsächliche Methoden . Meine Empfehlung: Befolgen Sie diese Beispiele, die zeigen, wie Sie eine Methode hinzufügen, die sich so genau wie möglich wie integrierte Methoden verhält.
user4642212

5

Ich habe dieses Problem gelöst, indem ich meine eigene Implementierung einer Basisliste erstellt habe, in der aufgezeichnet wird, wie viele Elemente im Objekt gespeichert sind. Es ist sehr einfach. Etwas wie das:

function BasicList()
{
   var items = {};
   this.count = 0;

   this.add = function(index, item)
   {
      items[index] = item;
      this.count++;
   }

   this.remove = function (index)
   {
      delete items[index];
      this.count--;
   }

   this.get = function(index)
   {
      if (undefined === index)
        return items;
      else
        return items[index];
   }
}

1
Interessante Alternative. Dadurch entfällt der Overhead einer aggregierten Zählfunktion, jedoch auf Kosten eines Funktionsaufrufs jedes Mal, wenn Sie ein Element hinzufügen oder entfernen, was leider schlimmer sein kann. Ich persönlich würde eine solche Listenimplementierung für die Datenkapselung und die benutzerdefinierten Methoden verwenden, die sie im Vergleich zu einem einfachen Array bereitstellen kann, aber nicht, wenn ich nur eine schnelle Elementzählung benötige.
Luc125

1
Ich mag deine Antwort, aber ich bin auch ein Lemming und habe eine positive Bewertung abgegeben. Dies ist ein interessantes Dilemma. Sie berücksichtigen kein Verhalten in Ihren Anweisungen, wie z. B. meine Situation, in der ich Ihre Antwort bereits positiv bewertet habe, aber dann werde ich angewiesen, auf "positiv zu klicken" und kann dies nicht. Die Anweisung schlägt stillschweigend fehl, aber ich entnehme Ihrem Inhalt hier auf SO, dass stillschweigendes Versagen nicht etwas ist, das Sie gerne mit Ihrem Code tun. Nur ein Kopf hoch.
L0j1k

1
Diese Antwort gefällt mir sehr gut, schöne Datenstruktur. Und wenn sich die Funktionsaufrufe beim Hinzufügen auf die Leistung auswirken würden, würde sich die Leistung erheblich steigern, wenn ein Objekt durchlaufen werden müsste. Dies sollte das schnellste Loop-Patten ermöglichenvar i = basiclist.count while(i--){...}
Lex

Sollte eine Basisliste nicht mindestens Basisprüfungen enthalten? Zum Beispiel prüfen, ob addein altes Element ersetzt wird oder ob removees durch einen nicht vorhandenen Index aufgerufen wird. Es ist auch nicht möglich zu überprüfen, ob die Liste einen bestimmten Index hat, wenn undefinedes sich um einen gültigen Elementwert handelt.
Robert

2
Eine Liste sollte geordnet und iterierbar sein. Die Daten werden in einem Objekt gespeichert, daher gibt es keine Garantie für die Reihenfolge der Elemente. Wie finden Sie die Länge einer Liste mit Löchern? this.count? Der höchste Indexwert? Wenn Sie jemals zwei Elemente am selben Index hinzufügen, geht die Anzahl in einen Fehlerzustand über.
Ultimate Gobblement

4

Sie können Object.keys(data).lengthdie Länge des JSON-Objekts mit Schlüsseldaten ermitteln


3

Für diejenigen, die Ext JS 4 in ihrem Projekt haben, können Sie Folgendes tun:

Ext.Object.getSize(myobj);

Dies hat den Vorteil, dass es auf allen Ext-kompatiblen Browsern (einschließlich IE6-IE8) funktioniert. Ich glaube jedoch, dass die Laufzeit nicht besser als O (n) ist, wie bei anderen Lösungsvorschlägen.


2

Sie können verwenden:

Object.keys(objectName).length; 

und

Object.values(objectName).length;

1

OP hat nicht angegeben, ob das Objekt eine Knotenliste ist. Wenn dies der Fall ist, können Sie einfach die Längenmethode direkt darauf verwenden. Beispiel:

buttons = document.querySelectorAll('[id=button)) {
console.log('Found ' + buttons.length + ' on the screen'); 

0

Wenn jQuery oben nicht funktioniert, versuchen Sie es

$(Object.Item).length

3
Es scheint, dass Object.Itemes nicht existiert
Luc125

-1

Ich denke nicht, dass dies möglich ist (zumindest nicht ohne einige Interna). Und ich glaube nicht, dass Sie durch die Optimierung viel gewinnen würden.


2
Die akzeptierte Antwort zeigt , dass dies kann getan werden, und Sie haben keinen Zusammenhang zu behaupten , dass es nichts zu gewinnen ist.
Conduit

-1

Ich versuche es allen Objekten wie folgt zur Verfügung zu stellen:

Object.defineProperty(Object.prototype, "length", {
get() {
    if (!Object.keys) {
        Object.keys = function (obj) {
            var keys = [],k;
            for (k in obj) {
                if (Object.prototype.hasOwnProperty.call(obj, k)) {
                    keys.push(k);
                }
            }
            return keys;
        };
    }
    return Object.keys(this).length;
},});

console.log({"Name":"Joe","Age":26}.length) //returns 2

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.