performance d'objet - Comment compter efficacement le nombre de clés / propriétés d'un objet en JavaScript?




parcourir tableau (16)

Comment j'ai résolu ce problème est de construire ma propre implémentation d'une liste de base qui conserve un enregistrement du nombre d'éléments sont 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];
   }
}

Quel est le moyen le plus rapide de compter le nombre de clés / propriétés d'un objet? Est-il possible de faire cela sans itération 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é magic __count__ , mais celle-ci a été supprimée quelque part autour de la version 4.)


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

Object.defineProperty (obj, prop, descriptor)

Vous pouvez soit 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 = "[email protected]";
myObj.length; //output: 2

Ajouté de cette façon, il ne sera pas affiché dans for..in boucles:

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

Sortie:

name:John Doe
email:[email protected]

Note: cela ne fonctionne pas dans les navigateurs <IE9.


J'essaie de le rendre accessible à tous les objets comme ceci:

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

Pour ceux qui ont Ext JS 4 dans leur projet, vous pouvez faire:

Ext.Object.getSize(myobj);

L'avantage de ceci est qu'il fonctionnera sur tous les navigateurs compatibles d'Ext (IE6-IE8 inclus), cependant, je crois que le temps d'exécution n'est pas meilleur que O (n) cependant, comme avec d'autres solutions suggérées.


Pour ceux qui ont Underscore.js inclus dans leur projet, vous pouvez faire:

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

ou style fonctionnel:

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

Si vous êtes réellement confronté à un problème de performances, je suggère 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é nommée (taille?) Appropriée.

Vous devez seulement calculer le nombre initial de propriétés une fois et passer à partir de là. S'il n'y a pas de problème de performance, ne vous embêtez pas. Enveloppez juste ce morceau de code dans une fonction getNumberOfProperties(object) et faites-en avec.


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 ensuite l'utiliser dans les anciens navigateurs:

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

L'implémentation Object standard ( Propriétés et méthodes internes de l'objet ES5.1 ) ne nécessite pas un Object pour suivre son nombre de clés / propriétés, il ne devrait donc pas y avoir de moyen standard pour déterminer la taille d'un Object sans itération explicite ou implicite de ses clés .

Voici donc les alternatives les plus couramment utilisées:

1. Object.keys d'ECMAScript ()

Object.keys(obj).length; Fonctionne en itérant en interne sur les clés pour calculer un tableau temporaire et renvoie sa longueur.

  • Avantages - syntaxe lisible et propre. Aucune bibliothèque ou 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 la bibliothèque

Beaucoup d'exemples basés sur des bibliothèques ailleurs dans ce sujet sont des idiomes utiles dans le contexte de leur bibliothèque. Du point de vue des performances, cependant, il n'y a rien à gagner par rapport à un code sans bibliothèque parfait puisque toutes ces méthodes de bibliothèque encapsulent en réalité une boucle Object.keys ES5 ou en boucle (native ou shimmed).

3. Optimisation d'une boucle for

La partie la plus lente d'une telle boucle for est généralement l'appel .hasOwnProperty() , en raison de l'en-tête de l'appel de fonction. Donc, quand je veux juste le nombre d'entrées d'un objet JSON, je saute l'appel .hasOwnProperty() si je sais qu'aucun code n'a étendu et ne va étendre Object.prototype .

Sinon, votre code pourrait être très légèrement optimisé en faisant k local ( var k ) et en utilisant l'opérateur prefix-increment ( ++count ) 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 hasOwnProperty :

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

Que ce soit plus rapide ou non sur un environnement donné est une question de benchmarking. Un gain de performance très limité peut être attendu de toute façon.




Comme indiqué par Avi Flax https://.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 à la place 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, il a le même navigateur que Object.keys

Cependant, dans la plupart des cas, vous ne voudrez peut-être pas inclure les non énumérables dans ce type d'opérations, mais il est toujours bon de connaître la différence;)


Je ne connais aucun moyen de le faire, mais pour minimiser les itérations, vous pouvez essayer de vérifier l'existence de __count__ et si elle n'existe pas (c'est-à-dire pas Firefox) alors vous pouvez parcourir l'objet et définissez-le pour une utilisation ultérieure, par exemple:

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

De cette façon, n'importe quel navigateur supportant __count__ l'utilisera, et les itérations ne seront effectuées que pour celles qui ne le font pas. 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(); }
}

De cette façon, chaque fois que vous faites référence à myobj. __count__ la fonction se déclenchera et recalculera.


Si vous utilisez Underscore.js vous pouvez utiliser _.size (merci @douwe):
_.size(obj)

Alternativement, vous pouvez également utiliser _.keys qui pourrait être plus clair pour certains:
_.keys(obj).length

Je recommande fortement Underscore, c'est une bibliothèque étroite pour faire beaucoup de choses de base. Chaque fois que possible, ils correspondent à ECMA5 et se reportent à l'implémentation native.

Sinon, je soutiens la réponse de @ Avi. Je l'ai édité pour ajouter un lien au document MDC qui inclut la méthode keys () que vous pouvez ajouter aux navigateurs non-ECMA5.


comme indiqué ci-dessus: Object.keys(obj).length

Mais: comme nous avons maintenant une vraie classe Map dans ES6, je suggest de l'utiliser au lieu d'utiliser les propriétés d'un objet.

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

Si jQuery ci-dessus ne fonctionne pas, essayez

$(Object.Item).length

Si vous souhaitez supprimer une propriété profondément imbriquée dans l'objet, vous pouvez utiliser la fonction récursive suivante avec chemin d'accès à la propriété comme second argument:

var deepObjectRemove = function(obj, path_to_key){
    if(path_to_key.length === 1){
        delete obj[path_to_key[0]];
        return true;
    }else{
        if(obj[path_to_key[0]])
            return deepObjectRemove(obj[path_to_key[0]], path_to_key.slice(1));
        else
            return false;
    }
};

Exemple:

var a = {
    level1:{
        level2:{
            level3: {
                level4: "yolo"
            }
        }
    }
};

deepObjectRemove(a, ["level1", "level2", "level3"]);
console.log(a);

//Prints {level1: {level2: {}}}




javascript performance properties count key