Come contare in modo efficiente il numero di chiavi / proprietà di un oggetto in JavaScript?


1545

Qual è il modo più veloce per contare il numero di chiavi / proprietà di un oggetto? È possibile farlo senza iterare sull'oggetto? cioè senza farlo

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

(Firefox ha fornito una __count__proprietà magica , ma questa è stata rimossa da qualche parte intorno alla versione 4.)



2
un benchmark delle prestazioni per diversi modi: jsben.ch/#/oSt2p
EscapeNetscape

1

Risposte:


2519

Per fare ciò in qualsiasi ambiente compatibile con ES5 , come Node , Chrome, IE 9+, Firefox 4+ o Safari 5+:

Object.keys(obj).length

8
Non solo Node.js, ma qualsiasi ambiente che supporti ES5
Yi Jiang,

59
A proposito ... ho appena eseguito alcuni test ... questo metodo viene eseguito in tempo O (n). Un ciclo for non è molto peggio di questo metodo. ** ** faccia triste stackoverflow.com/questions/7956554/...
BMiner

161
-1 (-200 se potessi) Questo non solo scorre nell'oggetto ma crea anche un array completamente nuovo con tutte le sue chiavi, quindi non riesce completamente a rispondere alla domanda.
GetFree,

42
Sembra molto più veloce di fare il for (almeno su Chrome 25): jsperf.com/count-elements-in-object
fserb

34
@GetFree Perché tanti pollici in su? Questo è sicuramente il modo più veloce in termini di codifica. Non sono richiesti metodi o librerie extra. In termini di velocità del codice, a quanto pare non è neanche male. Non è affatto un fallimento completo. 87 pollici in su falliscono per te.
Andrew

150

Puoi usare questo codice:

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

Quindi puoi usarlo anche nei browser più vecchi:

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

2
Qual è lo scopo del controllo (Object.prototype.hasOwnProperty.call(obj, k))?
styfle

14
@styfle Se si utilizza un ciclo for per scorrere le proprietà dell'oggetto, si ottengono anche le proprietà nella catena di prototipi. Ecco perché hasOwnPropertyè necessario il controllo . Restituisce solo le proprietà impostate sull'oggetto stesso.
Renaat De Muynck,

13
@styfle Per semplificare, potresti semplicemente scrivere obj.hasOwnProperty(k)(in realtà l'ho fatto nel mio post originale, ma l'ho aggiornato in seguito). hasOwnPropertyè disponibile su ogni oggetto perché fa parte del Objectprototipo del modello, ma nel raro caso in cui questo metodo venga rimosso o ignorato, si potrebbero ottenere risultati imprevisti. Chiamandolo da Object.prototypeesso lo rende un po 'più robusto. Il motivo dell'utilizzo callè perché si desidera invocare il metodo objanziché sul prototipo.
Renaat De Muynck,


1
@XavierDelamotte Hai assolutamente ragione. Mentre la mia versione funziona, è molto semplice e cita come esempio. Il codice di Mozilla è più sicuro. (PS: il tuo link è anche nella risposta accettata)
Renaat De Muynck

137

Se stai usando Underscore.js puoi usare _.size (grazie @douwe):
_.size(obj)

In alternativa puoi anche usare _.keys che potrebbe essere più chiaro per alcuni:
_.keys(obj).length

Consiglio vivamente Underscore, è una libreria ristretta per fare molte cose di base. Ove possibile, corrispondono a ECMA5 e rimandano all'implementazione nativa.

Altrimenti sostengo la risposta di @ Avi. L'ho modificato per aggiungere un collegamento al documento MDC che include il metodo keys () che è possibile aggiungere ai browser non ECMA5.


9
Se usi underscore.js, dovresti invece usare _.size. La cosa buona è che se in qualche modo si passa dall'array all'oggetto o viceversa, il risultato rimane lo stesso.
Douwe,

2
E dalla mia comprensione lodash è generalmente meglio di sottolineare (anche se fanno cose simili).
Merlyn Morgan-Graham,

1
@ MerlynMorgan-Graham, se ricordo bene, originariamente lodash è un fork di trattino basso ...
molson504x

6
_.keys(obj).lengthha funzionato meglio per me, perché il mio oggetto return a volte è una semplice stringa senza proprietà al suo interno. _.size(obj)mi restituisce la lunghezza della stringa, mentre _.keys(obj).lengthrestituisce 0.
Jacob Stamm,

O (n) complessità . Lodash e Underscore usano Object.keysinternamente. Underscore copia anche ogni chiave in un array all'interno di un for..inciclo se Object.keysnon definito.
N. Kudryavtsev,

82

L'implementazione standard degli oggetti ( proprietà e metodi interni agli oggetti ES5.1 ) non richiede Objectdi tenere traccia del numero di chiavi / proprietà, quindi non dovrebbe esserci un modo standard per determinare la dimensione di un Objectsenza iterare esplicitamente o implicitamente sulle sue chiavi.

Ecco quindi le alternative più comunemente utilizzate:

1. Object.keys () di ECMAScript

Object.keys(obj).length;Funziona ripetendo internamente le chiavi per calcolare un array temporaneo e restituirne la lunghezza.

  • Pro : sintassi leggibile e pulita. Nessuna libreria o codice personalizzato richiesto tranne uno spessore se il supporto nativo non è disponibile
  • Contro : sovraccarico di memoria dovuto alla creazione dell'array.

2. Soluzioni basate su libreria

Molti esempi basati su librerie altrove in questo argomento sono utili modi di dire nel contesto della loro biblioteca. Da un punto di vista delle prestazioni, tuttavia, non c'è nulla da guadagnare rispetto a un perfetto codice no-library poiché tutti quei metodi di libreria effettivamente incapsulano un for-loop o ES5 Object.keys(nativo o shimmed).

3. Ottimizzazione di un ciclo continuo

La parte più lenta di tale for-loop è generalmente la .hasOwnProperty()chiamata, a causa dell'overhead della chiamata di funzione. Quindi, quando voglio solo il numero di voci di un oggetto JSON, salto semplicemente la .hasOwnProperty()chiamata se so che nessun codice ha funzionato né si estenderà Object.prototype.

Altrimenti, il tuo codice potrebbe essere leggermente ottimizzato creando klocal ( var k) e usando l'operatore prefix-increment ( ++count) invece di postfix.

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

Un'altra idea si basa sulla memorizzazione nella cache del hasOwnPropertymetodo:

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

Se questo è più veloce o meno in un determinato ambiente è una questione di benchmarking. Ci si può aspettare comunque un rendimento molto limitato.


Perché var k in myobjmigliorare le prestazioni? Per quanto ne so, solo le funzioni dichiarano un nuovo ambito in JavaScript. I in-loop sono un'eccezione a questa regola?
Lars Gyrup Brink Nielsen,

1
È più veloce? for (var k in myobj) hasOwn.call(myobj, k) && ++count;cioè sostituire l'istruzione if con un semplice &&?
Hamza Kubba,

L'ultima cosa che puoi fare con Object.getOwnPropertyNames(obj).length:; molto più semplice.
Appassisci il

29

Se stai effettivamente riscontrando un problema di prestazioni, suggerirei di racchiudere le chiamate che aggiungono / rimuovono le proprietà a / dall'oggetto con una funzione che incrementa / decrementa anche una proprietà (size?) Appropriatamente denominata.

Devi solo calcolare il numero iniziale di proprietà una volta e passare da lì. Se non c'è un vero problema di prestazioni, non preoccuparti. Basta racchiudere quel pezzetto di codice in una funzione getNumberOfProperties(object)e terminare.


4
@hitautodestruct Perché offre una soluzione.
schiaccia il

@crush Questa risposta sembra suggerire cose da fare piuttosto che dare una soluzione diretta.
Hitautodestruct,

5
@hitautodestruct suggerisce una risposta: incrementare / decrementare un conteggio incapsulato con i metodi di aggiunta / rimozione. C'è un'altra risposta esattamente come questa sotto. L'unica differenza è che Confusion non ha offerto alcun codice. Le risposte non sono obbligate a fornire solo soluzioni di codice.
schiaccia l'

1
potrebbe non essere perfetto ... ma rispetto alle altre "risposte" questa potrebbe essere la soluzione migliore per alcune situazioni
d.raev

1
Finora questa è l'unica soluzione che vedo che è O (1) costante complessità delle prestazioni nel tempo, e quindi è l'unica soluzione che risponde al dettaglio della domanda "senza iterare" e dovrebbe quindi essere la vera risposta accettata. La maggior parte se non tutte le altre risposte non rispondono, perché offrono una complessità prestazionale temporale lineare O (n); questo vale anche per le soluzioni a 1 riga che chiamano qualcosa come una funzione .keys (), poiché tali chiamate di funzione sono O (n).
cellepo,

17

Come affermato da Avi Flax https://stackoverflow.com/a/4889658/1047014

Object.keys(obj).length

farà il trucco per tutte le proprietà enumerabili sul tuo oggetto ma per includere anche le proprietà non enumerabili che puoi invece usare Object.getOwnPropertyNames. Ecco la differenza:

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

Come indicato qui, questo ha lo stesso supporto del browser diObject.keys

Tuttavia, nella maggior parte dei casi, potresti non voler includere gli elementi non numerabili in questo tipo di operazioni, ma è sempre utile conoscere la differenza;)


1
Complimenti per Object.getOwnPropertyNamesaverlo menzionato , sei stato l'unico qui ...
Appassisci il

15

Non sono a conoscenza di alcun modo per farlo, tuttavia per mantenere le iterazioni al minimo, potresti provare a verificare l'esistenza di __count__e se non esiste (cioè non Firefox), puoi iterare l'oggetto e definire per un uso successivo, ad esempio:

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

In questo modo qualsiasi browser che supporti __count__lo userebbe e le iterazioni verrebbero eseguite solo per quelli che non lo fanno. Se il conteggio cambia e non puoi farlo, puoi sempre renderlo una funzione:

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

In questo modo ogni volta che fai riferimento a myobj. __count__la funzione verrà attivata e ricalcolata.


13
Si noti che Object.prototype.__count__viene rimosso in Gecko 1.9.3: whereswalden.com/2010/04/06/… conteggio -proprietà -di-oggetti-viene-
rimosso-

17
Ora che Firefox 4 è uscito, questa risposta è ormai obsoleta. Object.__count__se n'è andato, e anche il buon viaggio.
Yi Jiang,

1
Non direi che la risposta sia obsoleta. È ancora una strategia interessante per incapsulare un valore in una funzione.
devios1,

dovrebbe usare l'oggetto prototipo per estenderlo
Aaria Carter-Weir

13

Per iterare su Avi Flax, rispondere Object.keys (obj) .length è corretto per un oggetto che non ha funzioni legate ad esso

esempio:

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

contro

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);});}} */

passi per evitarlo:

  1. non inserire funzioni in un oggetto in cui si desidera contare il numero di chiavi

  2. usa un oggetto separato o crea un nuovo oggetto appositamente per le funzioni (se vuoi contare quante funzioni ci sono nel file usando Object.keys(obj).length)

anche sì, nel mio esempio ho usato il modulo _ o underscore di nodejs

la documentazione è disponibile qui http://underscorejs.org/ nonché la sua fonte su github e varie altre informazioni

E infine un'implementazione lodash https://lodash.com/docs#size

_.size(obj)


In risposta ai tuoi commenti su Array(obj).length: Non funziona. http://jsfiddle.net/Jhy8M/
Jamie Chong

sì, l'ho esaminato un po 'di più, finirò per rimuovere questa risposta se possibile o semplicemente per modificarla insieme
Belldandu,

Non vedo che questo abbia qualcosa a che fare con le funzioni, per esempio, e in Chrome non vedo affatto questo comportamento. Sospetto che ciò possa aver avuto a che fare con il comportamento predefinito di Object.defineProperty (): enumerabile che è false, anche se non ho ancora trovato alcuna documentazione su come var obj = { a: true, b: true }potrebbe differire var obj = {}; obj.a = true; obj.b = true;o semplicemente se una diversa interpretazione / semantica del W3 ha è stato adottato da Chrome.
Nolo,

10

come risposto sopra: Object.keys(obj).length

Ma: dato che ora abbiamo una vera classe Map in ES6, suggerirei di usarla invece di usare le proprietà di un oggetto.

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

8

Per coloro che hanno Underscore.js incluso nel loro progetto puoi fare:

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

o stile funzionale:

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

8

Ecco alcuni test delle prestazioni per tre metodi;

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

Object.keys (). Length

20.735 operazioni al secondo

Molto semplice e compatibile Funziona veloce ma costoso perché crea un nuovo array di chiavi, che poi viene buttato via.

return Object.keys(objectToRead).length;

scorrere tra i tasti

15.734 operazioni al secondo

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

Leggermente più lento, ma in nessun luogo vicino all'utilizzo della memoria, quindi probabilmente meglio se sei interessato a ottimizzare per dispositivi mobili o altri piccoli computer

Usando Mappa invece di Oggetto

953.839.338 operazioni al secondo

return mapToRead.size;

Fondamentalmente, Mappa tiene traccia delle sue dimensioni, quindi stiamo solo restituendo un campo numerico. Molto, molto più veloce di qualsiasi altro metodo. Se hai il controllo dell'oggetto, convertili invece in mappe.


6

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

Object.defineProperty (oggetto, oggetto, descrittore)

Puoi aggiungerlo a tutti i tuoi oggetti:

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

O un singolo oggetto:

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

Esempio:

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

Aggiunto in questo modo, non verrà visualizzato in for..in loop:

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

Produzione:

name:John Doe
email:leaked@example.com

Nota: non funziona nei browser <IE9.


Se si intende estendere i prototipi incorporati o eseguire il polyfill di una proprietà (es. Patch di scimmia), si prega di farlo correttamente: per compatibilità con il futuro, verificare se la proprietà esiste prima, quindi rendere la proprietà non enumerabile in modo che le proprie chiavi degli oggetti costruiti non sono inquinati. Per i metodi utilizzare metodi effettivi . Il mio consiglio: segui questi esempi che dimostrano come aggiungere un metodo che si comporta il più vicino possibile come i metodi integrati.
user4642212

5

Come ho risolto questo problema è costruire la mia implementazione di un elenco di base che tiene traccia di quanti elementi sono memorizzati nell'oggetto. È molto semplice. Qualcosa come questo:

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
Alternativa interessante Questo elimina il sovraccarico di una funzione di conteggio aggregato, ma a costo di una chiamata di funzione ogni volta che aggiungi o rimuovi un elemento, che purtroppo potrebbe essere peggio. Personalmente utilizzerei un'implementazione di tale elenco per l'incapsulamento dei dati e i metodi personalizzati che può fornire rispetto a un array semplice, ma non quando ho solo bisogno di un conteggio veloce degli articoli.
Luc125,

1
Mi piace la tua risposta, ma sono anche un lemming e un clic positivo. Questo presenta un dilemma interessante. Non stai spiegando alcun comportamento nelle tue istruzioni, come la mia situazione in cui ho già votato la tua risposta, ma poi mi viene chiesto di "fare clic su vota" e non posso. L'istruzione fallisce silenziosamente, ma ho raccolto dal tuo contenuto qui su SO che il fallimento silenzioso non è qualcosa che ti piace fare il tuo codice. Solo un avviso.
L0j1k,

1
Mi piace molto questa risposta, bella struttura dati. E se ci fosse un impatto sulle prestazioni con la funzione chiama in aggiunta, ci sarebbe un aumento delle prestazioni molto maggiore se si dovesse iterare su un oggetto. Ciò dovrebbe consentire il patten ad anello più velocevar i = basiclist.count while(i--){...}
Lex

Un elenco di base non dovrebbe almeno includere controlli di base? Come verificare se addsostituisce un vecchio elemento o se removeviene chiamato con un indice inesistente. Inoltre, non è possibile verificare se l'elenco ha un determinato indice se undefinedè un valore di elemento valido.
Robert,

2
Un elenco deve essere ordinato e ripetibile. I dati sono memorizzati in un oggetto, quindi non esiste alcuna garanzia sull'ordinamento degli elementi. Come trovi la lunghezza di un elenco con dei buchi? this.count? Il valore dell'indice più alto? Se aggiungi mai due elementi nello stesso indice, il conteggio passa in uno stato di errore.
Ultimate Gobblement

4

È possibile utilizzare Object.keys(data).lengthper trovare la lunghezza dell'oggetto JSON con dati chiave


3

Per quelli che hanno Ext JS 4 nel loro progetto puoi fare:

Ext.Object.getSize(myobj);

Il vantaggio di questo è che funzionerà su tutti i browser compatibili con Ext (IE6-IE8 incluso), tuttavia, credo che il tempo di esecuzione non sia migliore di O (n), tuttavia, come con altre soluzioni suggerite.


2

Puoi usare:

Object.keys(objectName).length; 

e

Object.values(objectName).length;

1

OP non ha specificato se l'oggetto è un nodeList, in tal caso è possibile utilizzare direttamente il metodo length . Esempio:

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

0

Se jQuery sopra non funziona, quindi provare

$(Object.Item).length

3
Sembra che Object.Itemnon esista
Luc125,

-1

Non credo sia possibile (almeno non senza usare alcuni interni). E non penso che guadagneresti molto ottimizzando questo.


2
La risposta accettata mostra che ciò può essere fatto e non hai alcun contesto per affermare che non c'è nulla da guadagnare.
Condotto

-1

Provo a renderlo disponibile a tutti gli oggetti in questo modo:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.