Comment compter efficacement le nombre de clés / propriétés d'un objet en JavaScript?
-
02-07-2019 - |
Question
Quel est le moyen le plus rapide de compter le nombre de clés / propriétés d’un objet? Est-il possible de le faire sans itérer sur l'objet? c'est-à-dire sans faire
var count = 0;
for (k in myobj) if (myobj.hasOwnProperty(k)) count++;
(Firefox a fourni une propriété magique __count__
, mais celle-ci a été supprimée quelque part autour de la version 4.)
La solution
Effectuez cette opération dans tout environnement compatible ES5 , tel que Noeud , Chrome, IE 9+, Firefox 4+ ou Safari 5+:
Object.keys(obj).length
- Compatibilité du navigateur
- Documentation sur Object.keys (inclut une méthode que vous pouvez utiliser). ajouter aux navigateurs non-ES5)
Autres conseils
Vous pouvez utiliser ce code:
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;
};
}
Vous pouvez également l'utiliser dans les anciens navigateurs:
var len = Object.keys(obj).length;
Si vous utilisez Underscore.js , vous pouvez utiliser _. taille (merci @douwe):
_.size(obj)
Vous pouvez également utiliser _.keys , ce qui pourrait être plus clair pour certains:
_.keys(obj).length
Je recommande vivement Underscore, une bibliothèque compacte permettant de faire beaucoup de choses de base. Dans la mesure du possible, ils correspondent à ECMA5 et s’appliquent à l’implémentation native.
Sinon, je soutiens la réponse de @ Avi. Je l'ai modifiée pour ajouter un lien vers la documentation MDC, qui inclut la méthode keys (), que vous pouvez ajouter aux navigateurs non-ECMA5.
L'implémentation d'objet standard ( Propriétés internes de l'objet ES5.1 and Methods ) ne nécessite pas de Object
suivi de son nombre de clés / propriétés; il ne doit donc exister aucun moyen standard de déterminer la taille d'un Object.keys(obj).length;
sans itérer explicitement ou implicitement sur ses clés.
Voici donc les alternatives les plus couramment utilisées:
1. Object.keys () d'ECMAScript
Object.keys
Fonctionne en en interne en parcourant les clés pour calculer un tableau temporaire et renvoie sa longueur.
- Avantages : syntaxe lisible et claire. Aucune bibliothèque ni code personnalisé requis, sauf un shim si le support natif n'est pas disponible
- Inconvénients : surcharge de mémoire due à la création du tableau.
2. Solutions basées sur des bibliothèques
De nombreux exemples basés sur les bibliothèques ailleurs dans cette rubrique sont des idiomes utiles dans le contexte de leur bibliothèque. Du point de vue des performances, cependant, il n’ya rien à gagner par rapport à un code parfait sans bibliothèque car toutes ces méthodes de bibliothèque encapsulent en fait une boucle for ou ES5 .hasOwnProperty()
(natif ou shimmed).
3. Optimiser une boucle for
La partie la plus lente d'une telle boucle for est généralement l'appel Object.prototype
en raison de la surcharge de l'appel de fonction. Ainsi, lorsque je veux juste le nombre d'entrées d'un objet JSON, je passe simplement l'appel k
si je sais qu'aucun code n'a été ni ne sera étendu var k
.
Sinon, votre code pourrait être très légèrement optimisé en rendant ++count
local (hasOwnProperty
) et en utilisant l'opérateur prefix-increment (<=>) au lieu de postfix.
var count = 0;
for (var k in myobj) if (myobj.hasOwnProperty(k)) ++count;
Une autre idée repose sur la mise en cache de la <=> méthode:
var hasOwn = Object.prototype.hasOwnProperty;
var count = 0;
for (var k in myobj) if (hasOwn.call(myobj, k)) ++count;
Que cela soit plus rapide ou non sur un environnement donné est une question d'analyse comparative. De toute façon, on peut s’attendre à un gain de performance très limité.
Si vous rencontrez réellement un problème de performances, je vous suggèrerais d'encapsuler les appels qui ajoutent / suppriment des propriétés à / de l'objet avec une fonction qui incrémente / décrémente également une propriété (size?) nommée de manière appropriée.
Il vous suffit de calculer le nombre initial de propriétés une fois, puis de continuer à partir de là. S'il n'y a pas de problème de performances, ne vous inquiétez pas. Il suffit d’envelopper ce morceau de code dans une fonction getNumberOfProperties(object)
et d’en finir.
Je ne suis au courant d'aucun moyen de le faire. Cependant, pour limiter les itérations au minimum, vous pouvez vérifier l'existence de __count__
et si elle n'existe pas (c'est-à-dire pas Firefox), vous pouvez itérer sur l'objet et le définir pour une utilisation ultérieure, par exemple:
if (myobj.__count__ === undefined) {
myobj.__count__ = ...
}
Ainsi, tout navigateur prenant en charge <=> l’utiliserait, et les itérations ne seraient effectuées que pour les autres. Si le nombre change et que vous ne pouvez pas le faire, vous pouvez toujours en faire une fonction:
if (myobj.__count__ === undefined) {
myobj.__count__ = function() { return ... }
myobj.__count__.toString = function() { return this(); }
}
Ainsi, chaque fois que vous référencez myobj. <=> la fonction sera déclenchée et recalculée.
Comme indiqué par Avi Flax, https://stackoverflow.com/a/4889658/1047014
Object.keys(obj).length
fera l'affaire pour toutes les propriétés énumérables sur votre objet, mais pour inclure également les propriétés non énumérables, vous pouvez utiliser le Object.getOwnPropertyNames
. Voici la différence:
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
Comme indiqué ici , le navigateur prend en charge le même support que Object.keys
Cependant, dans la plupart des cas, vous ne voudrez peut-être pas inclure les éléments non énumérables dans ce type d'opérations, mais il est toujours bon de connaître la différence;)
Pour itérer sur Avi Flax, répondez Object.keys (obj) .length est correct pour un objet qui ne possède pas de fonctions liées à celui-ci
exemple:
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);});}} */
étapes pour éviter cela:
-
ne placez pas de fonctions dans un objet pour lequel vous souhaitez compter le nombre de clés
-
utilisez un objet séparé ou créez un nouvel objet spécialement pour les fonctions (si vous souhaitez compter le nombre de fonctions présentes dans le fichier à l'aide de
Object.keys(obj).length
)
aussi oui j'ai utilisé le _ ou le module de soulignement de nodejs dans mon exemple
Ladocumentation est disponible ici http://underscorejs.org/ , ainsi que sa source sur github et divers autres info
Et enfin, une implémentation de lodash https://lodash.com/docs#size
_.size(obj)
Pour ceux qui ont Underscore.js inclus dans leur projet, vous pouvez faire:
_({a:'', b:''}).size() // => 2
ou style fonctionnel:
_.size({a:'', b:''}) // => 2
Object.defineProperty (obj, prop, descriptor)
Vous pouvez l'ajouter à tous vos objets:
Object.defineProperty(Object.prototype, "length", {
enumerable: false,
get: function() {
return Object.keys(this).length;
}
});
Ou un seul objet:
var myObj = {};
Object.defineProperty(myObj, "length", {
enumerable: false,
get: function() {
return Object.keys(this).length;
}
});
Exemple:
var myObj = {};
myObj.name = "John Doe";
myObj.email = "leaked@example.com";
myObj.length; //output: 2
Ajouté de cette façon, il ne sera pas affiché dans les boucles for..in:
for(var i in myObj) {
console.log(i + ":" + myObj[i]);
}
Sortie:
name:John Doe
email:leaked@example.com
Remarque: cela ne fonctionne pas dans < Navigateurs IE9.
Pour résoudre ce problème, j'ai créé ma propre implémentation d'une liste de base qui enregistre le nombre d'éléments stockés dans l'objet. C'est très simple. Quelque chose comme ça:
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];
}
}
Pour ceux qui ont Ext JS 4 dans leur projet, vous pouvez faire:
Ext.Object.getSize(myobj);
L'avantage de cela est qu'il fonctionnera sur tous les navigateurs compatibles Ext (IE6-IE8 inclus), cependant, je pense que le temps d'exécution n'est pas meilleur que O (n), contrairement à d'autres solutions suggérées.
Vous pouvez utiliser Object.keys(data).length
pour rechercher la longueur d'un objet JSON contenant des données de clé
Si jQuery ci-dessus ne fonctionne pas, essayez
$(Object.Item).length
OP n'a pas spécifié si l'objet est une liste de noeuds. Si c'est le cas, vous pouvez simplement utiliser la méthode length dessus. Exemple:
buttons = document.querySelectorAll('[id=button)) {
console.log('Found ' + buttons.length + ' on the screen');
Je ne pense pas que ce soit possible (du moins pas sans utiliser des composants internes). Et je ne pense pas que vous gagneriez beaucoup en optimisant cela.
J'essaie de le rendre disponible pour tous les objets comme celui-ci:
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
Google Closure a une fonction intéressante pour cela ... goog.object.getCount (obj)
. Vous pouvez utiliser:
Object.keys(objectName).length;
& Object.values(objectName).length;