¿Cómo contar eficientemente el número de claves / propiedades de un objeto en JavaScript?

1545

¿Cuál es la forma más rápida de contar el número de claves / propiedades de un objeto? ¿Es posible hacer esto sin iterar sobre el objeto? es decir, sin hacer

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

(Firefox proporcionó una __count__propiedad mágica , pero esta se eliminó en algún lugar alrededor de la versión 4.)

mjs
fuente
3
Relacionado: stackoverflow.com/questions/5223/…
ripper234
2
una referencia de rendimiento para diferentes maneras: jsben.ch/#/oSt2p
EscapeNetscape
1
Posible duplicado de la longitud de un objeto JavaScript
Adam Katz

Respuestas:

2519

Para hacer esto en cualquier entorno compatible con ES5 , como Node , Chrome, IE 9+, Firefox 4+ o Safari 5+:

Object.keys(obj).length
Avi Flax
fuente
8
No solo Node.js, sino cualquier entorno que admita ES5
Yi Jiang
59
Por cierto ... solo ejecuté algunas pruebas ... este método se ejecuta en tiempo O (n). Un bucle for no es mucho peor que este método. ** cara triste ** stackoverflow.com/questions/7956554/…
BMiner
161
-1 (-200 si pudiera) Esto no solo recorre el objeto en iteración sino que también crea una matriz completamente nueva con todas sus claves, por lo que falla completamente al responder la pregunta.
GetFree
42
Parece mucho más rápido que hacer for (al menos en Chrome 25): jsperf.com/count-elements-in-object
fserb
34
@GetFree ¿Por qué tantos pulgares arriba? Esta es definitivamente la forma más rápida en términos de codificación. No se requieren métodos o bibliotecas adicionales. En términos de velocidad de código, aparentemente tampoco es tan malo. No es un completo fracaso en absoluto. 87 pulgares arriba falla por ti.
Andrew
150

Podrías usar este código:

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

Entonces puede usar esto también en navegadores antiguos:

var len = Object.keys(obj).length;
Renaat De Muynck
fuente
2
¿Cuál es el propósito del cheque (Object.prototype.hasOwnProperty.call(obj, k))?
styfle
14
@styfle Si usa un bucle for para iterar sobre las propiedades del objeto, también obtendrá las propiedades en la cadena del prototipo. Por eso hasOwnPropertyes necesario verificar . Solo devuelve propiedades establecidas en el objeto mismo.
Renaat De Muynck
13
@styfle Para hacerlo más simple, simplemente podría escribir obj.hasOwnProperty(k)(en realidad lo hice en mi publicación original, pero lo actualicé más tarde). hasOwnPropertyestá disponible en todos los objetos porque es parte del Objectprototipo del, pero en el raro caso de que este método se elimine o se anule, puede obtener resultados inesperados. Al llamarlo, Object.prototypelo hace un poco más robusto. La razón para usarlo calles porque desea invocar el método en objlugar del prototipo.
Renaat De Muynck
66
¿No sería mejor usar esta versión? developer.mozilla.org/en-US/docs/JavaScript/Reference/…
Xavier Delamotte
1
@XavierDelamotte Tienes toda la razón. Si bien mi versión funciona, es muy básica y es un ejemplo. El código de Mozilla es más seguro. (PD: su enlace también está en la respuesta aceptada)
Renaat De Muynck
137

Si está utilizando Underscore.js puede usar _.size (gracias @douwe):
_.size(obj)

Alternativamente, también puede usar _.keys que podrían ser más claros para algunos:
_.keys(obj).length

Recomiendo mucho Underscore, es una biblioteca compacta para hacer muchas cosas básicas. Siempre que sea posible, coinciden con ECMA5 y difieren de la implementación nativa.

De lo contrario, apoyo la respuesta de @ Avi. Lo edité para agregar un enlace al documento de MDC que incluye el método de claves () que puede agregar a los navegadores que no son ECMA5.

studgeek
fuente
99
Si usa underscore.js, debería usar _.size en su lugar. Lo bueno es que si de alguna manera cambia de matriz a objeto o viceversa, el resultado sigue siendo el mismo.
douwe
2
Y, según tengo entendido, lodash es generalmente mejor que el subrayado (aunque hacen cosas similares).
Merlyn Morgan-Graham
1
@ MerlynMorgan-Graham, si mal no recuerdo, lodash es originalmente una bifurcación de subrayado ...
molson504x
66
_.keys(obj).lengthfuncionó mejor para mí, porque mi objeto de retorno es a veces una cadena simple sin propiedades dentro de él. _.size(obj)me devuelve la longitud de la cadena, mientras _.keys(obj).lengthdevuelve 0.
Jacob Stamm
O (n) complejidad . Lodash y Underscore usan Object.keysinternamente. El subrayado también copia cada clave en una matriz dentro de un for..inbucle si Object.keysno está definida.
N. Kudryavtsev
82

La implementación estándar de objetos ( Propiedades y métodos internos de objetos ES5.1 ) no requiere un Objectseguimiento de su número de claves / propiedades, por lo que no debe haber una forma estándar de determinar el tamaño de un Objectsin iterar explícita o implícitamente sobre sus claves.

Aquí están las alternativas más utilizadas:

1. Object.keys de ECMAScript ()

Object.keys(obj).length;Funciona iterando internamente sobre las teclas para calcular una matriz temporal y devuelve su longitud.

  • Pros : sintaxis legible y limpia. No se requiere biblioteca o código personalizado, excepto una cuña si el soporte nativo no está disponible
  • Contras : sobrecarga de memoria debido a la creación de la matriz.

2. Soluciones basadas en bibliotecas

Muchos ejemplos basados ​​en bibliotecas en otras partes de este tema son expresiones idiomáticas útiles en el contexto de su biblioteca. Sin embargo, desde el punto de vista del rendimiento, no hay nada que ganar en comparación con un código perfecto sin biblioteca, ya que todos esos métodos de biblioteca realmente encapsulan ya sea for-loop o ES5 Object.keys(nativo o shimmed).

3. Optimizar un ciclo for

La parte más lenta de tal ciclo for es generalmente la .hasOwnProperty()llamada, debido a la sobrecarga de la llamada de función. Entonces, cuando solo quiero la cantidad de entradas de un objeto JSON, simplemente salteo la .hasOwnProperty()llamada si sé que ningún código se extendió ni se extenderá Object.prototype.

De lo contrario, su código podría optimizarse ligeramente haciendo klocal ( var k) y utilizando el operador de incremento de prefijo ( ++count) en lugar de postfix.

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

Otra idea se basa en el almacenamiento en caché del hasOwnPropertymétodo:

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

Si esto es más rápido o no en un entorno determinado es una cuestión de evaluación comparativa. Se puede esperar una ganancia de rendimiento muy limitada de todos modos.

Luc125
fuente
¿Por qué var k in myobjaumentaría el rendimiento? Hasta donde sé, solo las funciones declaran un nuevo alcance en JavaScript. ¿Los in-loops son una excepción a esta regla?
Lars Gyrup Brink Nielsen
1
¿Es esto más rápido? for (var k in myobj) hasOwn.call(myobj, k) && ++count;es decir, reemplazar la declaración if con un simple &&?
Hamza Kubba
Lo último que puedes hacer con Object.getOwnPropertyNames(obj).length:; Mucho más simple.
marchita
29

Si realmente se encuentra con un problema de rendimiento, le sugiero que ajuste las llamadas que agregan / eliminan propiedades al / del objeto con una función que también incrementa / disminuye una propiedad con el nombre apropiado (¿tamaño?).

Solo necesita calcular el número inicial de propiedades una vez y continuar desde allí. Si no hay un problema de rendimiento real, no se moleste. Simplemente envuelva ese bit de código en una función getNumberOfProperties(object)y termine con él.

Confusión
fuente
44
@hitautodestruct Porque él ofrece una solución.
aplastar
@crush Esta respuesta parece sugerir cosas que hacer en lugar de dar una solución directa.
hitautodestruct
55
@hitautodestruct sugiere una respuesta: aumentar / disminuir un recuento encapsulado con los métodos de agregar / quitar. Hay otra respuesta exactamente como esta a continuación. La única diferencia es que Confusion no ofreció ningún código. Las respuestas no tienen el mandato de proporcionar soluciones de código solamente.
aplastar
1
puede que no sea perfecto ... pero comparado con las otras "respuestas", esta puede ser la mejor solución para algunas situaciones
d.raev
1
Hasta ahora, esta es la única solución que veo que es O (1) complejidad de rendimiento de tiempo constante y, por lo tanto, es la única solución que responde los detalles de la pregunta "sin iterar" y, por lo tanto, debe ser la respuesta aceptada tru. La mayoría, si no todas las demás respuestas, no responden eso, porque ofrecen una complejidad de rendimiento de tiempo lineal O (n); ese es también el caso de las soluciones de 1 línea que llaman algo así como una función .keys (), ya que tales llamadas a funciones son O (n).
cellepo
17

Según lo indicado por Avi Flax https://stackoverflow.com/a/4889658/1047014

Object.keys(obj).length

hará el truco para todas las propiedades enumerables en su objeto, pero para incluir también las propiedades no enumerables, puede usar el Object.getOwnPropertyNames. Aquí está la diferencia:

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

Como se indicó aquí, este tiene el mismo soporte de navegador queObject.keys

Sin embargo, en la mayoría de los casos, es posible que no desee incluir los no enumerables en este tipo de operaciones, pero siempre es bueno saber la diferencia;)

BenderTheOffender
fuente
1
Gracias por mencionar que Object.getOwnPropertyNamesfuiste el único aquí ...
marchitará
15

No conozco ninguna forma de hacer esto, sin embargo, para mantener las iteraciones al mínimo, podría intentar verificar la existencia de __count__y si no existe (es decir, no Firefox), podría iterar sobre el objeto y definir para su uso posterior, por ejemplo:

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

De esta forma, cualquier navegador compatible __count__lo usaría, y las iteraciones solo se llevarían a cabo para aquellos que no lo hacen. Si el recuento cambia y no puede hacer esto, siempre podría hacerlo una función:

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

De esta manera cada vez que haga referencia a myobj. __count__la función se activará y volverá a calcular.

Luke Bennett
fuente
13
Tenga en cuenta que Object.prototype.__count__se está eliminando en Gecko 1.9.3: whereswalden.com/2010/04/06/… cuenta -propiedad-de-objetos-se-está-eliminando /
dshaw
17
Ahora que Firefox 4 está fuera, esta respuesta ahora está obsoleta. Object.__count__se ha ido, y buen viaje también.
Yi Jiang el
1
No diría que la respuesta es obsoleta. Sigue siendo una estrategia interesante encapsular un valor en una función.
devios1
debería estar usando el objeto prototipo para extenderlo
Aaria Carter-Weir
13

Para iterar en Avi Flax, responda Object.keys (obj) .length es correcto para un objeto que no tiene funciones vinculadas

ejemplo:

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

versus

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

pasos para evitar esto:

  1. no coloque funciones en un objeto en el que desee contar el número de claves

  2. use un objeto separado o cree un nuevo objeto específicamente para funciones (si desea contar cuántas funciones hay en el archivo usando Object.keys(obj).length)

También sí, utilicé el módulo _ o subrayado de nodejs en mi ejemplo

la documentación se puede encontrar aquí http://underscorejs.org/ así como su fuente en github y otra información

Y finalmente una implementación lodash https://lodash.com/docs#size

_.size(obj)

Belldandu
fuente
En respuesta a sus comentarios sobre Array(obj).length: No funciona. http://jsfiddle.net/Jhy8M/
Jamie Chong
sí que se veía en él un poco más im va a terminar la eliminación de esta respuesta si es posible o simplemente editando todo junto
Belldandu
No veo que esto tenga algo que ver con las funciones, por ejemplo, y en Chrome no veo este comportamiento en absoluto. Sospecho que esto puede haber tenido que ver con el comportamiento predeterminado de Object.defineProperty (): enumerable que es false, aunque yo aún no he encontrado ninguna documentación sobre cómo var obj = { a: true, b: true }pueden diferir de var obj = {}; obj.a = true; obj.b = true;o simplemente si una interpretación diferente / semántica de la W3 tiene sido adoptado por Chrome.
Nolo
10

como se respondió anteriormente: Object.keys(obj).length

Pero: como ahora tenemos una clase de Mapa real en ES6, sugeriría usarla en lugar de usar las propiedades de un objeto.

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

Para aquellos que tienen Underscore.js incluido en su proyecto, pueden hacer:

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

o estilo funcional:

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

Aquí hay algunas pruebas de rendimiento para tres métodos;

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

Object.keys (). Length

20,735 operaciones por segundo

Muy simple y compatible. Corre rápido pero costoso porque crea una nueva matriz de claves, que luego se descarta.

return Object.keys(objectToRead).length;

recorrer las teclas

15,734 operaciones por segundo

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

Ligeramente más lento, pero nada cerca del uso de memoria, por lo que probablemente sea mejor si está interesado en optimizar para dispositivos móviles u otras máquinas pequeñas

Usar mapa en lugar de objeto

953,839,338 operaciones por segundo

return mapToRead.size;

Básicamente, Map rastrea su propio tamaño, por lo que solo estamos devolviendo un campo numérico. Mucho, mucho más rápido que cualquier otro método. Si tiene control del objeto, conviértalos en mapas.

Steve Cooper
fuente
6

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

Object.defineProperty (obj, prop, descriptor)

Puedes agregarlo a todos tus objetos:

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

O un solo objeto:

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

Ejemplo:

var myObj = {};
myObj.name  = "John Doe";
myObj.email = "[email protected]";
myObj.length; //output: 2

Agregado de esa manera, no se mostrará en ... en bucles:

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

Salida:

name:John Doe
email:leaked@example.com

Nota: no funciona en los navegadores <IE9.

lepe
fuente
Si va a extender prototipos incorporados o rellenar una propiedad (es decir, parche de mono), hágalo correctamente: para la compatibilidad hacia adelante, verifique si la propiedad existe primero, luego haga que la propiedad no sea enumerable para que las propias claves de los objetos construidos no están contaminados. Para los métodos, use métodos reales . Mi recomendación: siga estos ejemplos que demuestran cómo agregar un método que se comporte de la manera más parecida posible a los métodos integrados.
user4642212
5

Cómo he resuelto este problema es crear mi propia implementación de una lista básica que mantiene un registro de cuántos elementos están almacenados en el objeto. Es muy simple. Algo como esto:

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];
   }
}
Haga clic en Upvote
fuente
1
Alternativa interesante Esto elimina la sobrecarga de una función de conteo agregado, pero a costa de una llamada a la función cada vez que agrega o elimina un elemento, lo que desafortunadamente puede ser peor. Personalmente, usaría una implementación de esta lista para la encapsulación de datos y los métodos personalizados que puede proporcionar en comparación con una matriz simple, pero no cuando solo necesito un conteo rápido de elementos.
Luc125
1
Me gusta tu respuesta, pero también soy un lemming e hice clic en la votación. Esto presenta un dilema interesante. No estás contando algún comportamiento en tus instrucciones, como mi situación en la que ya he votado tu respuesta, pero luego se me indica que "hagas clic a favor" y no puedo. La instrucción falla en silencio, pero deduzco de su contenido aquí en SO que fallar en silencio no es algo que le gusta que haga su código. Solo un aviso.
L0j1k
1
Realmente me gusta esta respuesta, buena estructura de datos. Y si hubiera un impacto en el rendimiento con las llamadas de función en add, habría un aumento de rendimiento mucho mayor si tuviera que iterar sobre un objeto. Esto debería permitir el patten de bucle más rápidovar i = basiclist.count while(i--){...}
Lex
¿No debería una lista básica al menos incluir controles básicos? Como verificar si addreemplaza un elemento antiguo o si removese llama con un índice no existente. Además, no es posible verificar si la lista tiene un índice dado si undefinedes un valor de elemento válido.
Robert
2
Una lista debe ser ordenada e iterable. Los datos se almacenan en un objeto, por lo que no hay garantía sobre el orden de los elementos. ¿Cómo encuentras la longitud de una lista con agujeros? this.count? ¿El valor de índice más alto? Si alguna vez agrega dos elementos en el mismo índice, el conteo entra en un estado de error.
Ultimate Gobblement
4

Puede usar Object.keys(data).lengthpara encontrar la longitud del objeto JSON que tiene datos clave

Codemaker
fuente
3

Para aquellos que tienen Ext JS 4 en su proyecto, pueden hacer:

Ext.Object.getSize(myobj);

La ventaja de esto es que funcionará en todos los navegadores compatibles con Ext (incluido IE6-IE8), sin embargo, creo que el tiempo de ejecución no es mejor que O (n), como ocurre con otras soluciones sugeridas.

Mark Rhodes
fuente
2

Puedes usar:

Object.keys(objectName).length; 

y

Object.values(objectName).length;
Fayaz
fuente
1

OP no especificó si el objeto es una lista de nodos, si es así, puede usar el método de longitud directamente. Ejemplo:

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

Si jQuery anterior no funciona, intente

$(Object.Item).length
codejoecode
fuente
3
Parece que Object.Itemno existe
Luc125
-1

No creo que esto sea posible (al menos no sin usar algunos elementos internos). Y no creo que ganarías mucho optimizando esto.

una mezcla
fuente
2
La respuesta aceptada muestra que esto se puede hacer, y no tiene contexto para afirmar que no hay nada que ganar.
Conducto
-1

Intento ponerlo a disposición de todos los objetos como este:

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
Taquatech
fuente