Как эффективно подсчитать количество ключей / свойств объекта в JavaScript?

Какой самый быстрый способ подсчитать количество ключей / свойств объекта? Можно ли сделать это без перебора объекта? т.е. без выполнения

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

(Firefox обеспечил магию __count__ свойство, но это было удалено где-то около версии 4.)

24 ответа

Решение

Чтобы сделать это в любой ES5-совместимой среде, такой как Node, Chrome, IE 9+, FF 4+ или Safari 5+:

Object.keys(obj).length

Вы можете использовать этот код:

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

Затем вы можете использовать это в старых браузерах:

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

Если вы используете http://underscorejs.org/, вы можете использовать _.size (спасибо @douwe):
_.size(obj)

В качестве альтернативы вы также можете использовать _.keys, которые могут быть более понятными для некоторых:
_.keys(obj).length

Я настоятельно рекомендую Underscore, его компактную библиотеку для выполнения множества базовых задач. По возможности, они соответствуют ECMA5 и относятся к собственной реализации.

В противном случае я поддерживаю ответ @Avi. Я отредактировал его, добавив ссылку на документ MDC, который включает метод keys(), который вы можете добавить в браузеры не-ECMA5.

Вот несколько тестов производительности для трех методов;

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

Object.keys(). Length

20735 операций в секунду

Очень просто и совместимо. Работает быстро, но дорого, поскольку создает новый массив ключей, который затем выбрасывается.

return Object.keys(objectToRead).length;

пройтись по клавишам

15734 операции в секунду

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

Немного медленнее, но совсем не по использованию памяти, поэтому, вероятно, лучше, если вы заинтересованы в оптимизации для мобильных или других небольших машин.

Использование карты вместо объекта

953839338 операций в секунду

return mapToRead.size;

По сути, Map отслеживает свой размер, поэтому мы просто возвращаем числовое поле. Намного, намного быстрее, чем любой другой метод. Если у вас есть контроль над объектом, вместо этого преобразуйте их в карты.

Стандартная реализация объекта ( внутренние свойства и методы объекта ES5.1) не требует Object отслеживать его количество ключей / свойств, поэтому не должно быть никакого стандартного способа определения размера Object без явного или неявного перебора его ключей.

Итак, вот наиболее часто используемые альтернативы:

1. ECMAScript Object.keys()

Object.keys(obj).length; Работает путем внутренней перебора ключей для вычисления временного массива и возвращает его длину.

  • Плюсы - Читаемый и чистый синтаксис. Не требуется никакой библиотеки или пользовательского кода, кроме прокладки, если встроенная поддержка недоступна
  • Минусы - накладные расходы памяти из-за создания массива.

2. Библиотечные решения

Многие основанные на библиотеках примеры в других частях этой темы являются полезными идиомами в контексте их библиотеки. Однако с точки зрения производительности выиграть нечего по сравнению с идеальным кодом без библиотек, поскольку все эти библиотечные методы фактически инкапсулируют цикл for или ES5. Object.keys (родной или подкладочный).

3. Оптимизация цикла for

Самая медленная часть такого цикла, как правило, .hasOwnProperty() вызов из-за накладных расходов на вызов функции. Поэтому, когда я просто хочу количество записей объекта JSON, я просто пропускаю .hasOwnProperty() позвоните, если я знаю, что ни один код не сделал и не будет расширяться Object.prototype,

В противном случае ваш код может быть немного оптимизирован, если k местный (var k) и с помощью префиксного оператора приращения (++count) вместо постфикса.

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

Другая идея опирается на кэширование hasOwnProperty метод:

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

Является ли это быстрее или нет в данной среде - это вопрос сравнительного анализа. В любом случае можно ожидать очень ограниченного прироста производительности.

Если вы действительно столкнулись с проблемой производительности, я бы предложил обернуть вызовы, которые добавляют / удаляют свойства к / из объекта, с функцией, которая также увеличивает / уменьшает свойство с соответствующим именем (size?).

Вам нужно только рассчитать начальное количество свойств один раз и двигаться дальше. Если нет реальной проблемы с производительностью, не беспокойтесь. Просто оберните этот фрагмент кода в функцию getNumberOfProperties(object) и покончим с этим.

это работает как для массивов, так и для объектов

      function count(obj){
        return Object.keys(obj).length
     }

Как ответили выше: Object.keys(obj).length

Но: поскольку у нас теперь есть реальный класс Map в ES6, я бы предложил использовать его вместо использования свойств объекта.

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

Как заявил Ави Лен /questions/24714349/kak-effektivno-podschitat-kolichestvo-klyuchej-svojstv-obekta-v-javascript/24714373#24714373

Object.keys(obj).length

будет делать трюк для всех перечисляемых свойств вашего объекта, но также включать неперечислимые свойства, которые вы можете вместо этого использовать Object.getOwnPropertyNames, Вот разница:

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

Как указано здесь, он имеет ту же поддержку браузера, что и Object.keys

Тем не менее, в большинстве случаев, вы можете не захотеть включать не перечисляемые в этот тип операций, но всегда полезно знать разницу;)

Итерации по Avi Flax: ответ Object.keys(obj).length правильный для объекта, к которому не привязаны функции

пример:

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

против

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

шаги, чтобы избежать этого:

  1. не помещайте функции в объект, который вы хотите посчитать количество ключей в

  2. использовать отдельный объект или создать новый объект специально для функций (если вы хотите подсчитать, сколько функций в файле, используя Object.keys(obj).length)

также да, я использовал _ или модуль подчеркивания из nodejs в моем примере

документацию можно найти здесь http://underscorejs.org/ а также ее источник на github и другую другую информацию

И, наконец, реализация lodash https://lodash.com/docs

_.size(obj)

Я не знаю ни одного способа сделать это, однако, чтобы свести итерации к минимуму, вы можете попробовать проверить наличие __count__ и если он не существует (то есть не Firefox), то вы можете перебрать объект и определить его для дальнейшего использования, например:

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

Таким образом, любой браузер поддерживает __count__ будет использовать это, и итерации будут выполняться только для тех, кто этого не делает. Если счетчик меняется и вы не можете этого сделать, вы всегда можете сделать его функцией:

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

Таким образом, в любое время вы ссылаетесь на myobj.__count__ функция сработает и пересчитает.

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

Object.defineProperty (obj, prop, descriptor)

Вы можете добавить его ко всем вашим объектам:

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

Или один объект:

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

Пример:

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

Добавленный таким образом, он не будет отображаться в циклах for..in:

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

Выход:

name:John Doe
email:leaked@example.com

Примечание: он не работает в браузерах

Для тех, у кого есть Underscore.js, включенный в их проект, вы можете сделать:

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

или функциональный стиль:

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

Я решил эту проблему, создав собственную реализацию базового списка, который хранит записи о том, сколько элементов хранится в объекте. Это очень просто. Что-то вроде этого:

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

Для тех, кто имеет Ext JS 4 в своем проекте, вы можете сделать:

Ext.Object.getSize(myobj);

Преимущество этого заключается в том, что он будет работать во всех совместимых с Ext браузерах (включая IE6-IE8), однако, я считаю, что время выполнения не лучше, чем O(n), как и в случае других предлагаемых решений.

Ты можешь использовать: Object.keys(objectName).length; & Object.values(objectName).length;

Тест

Если jQuery выше не работает, то попробуйте

$(Object.Item).length

OP не указал, является ли объект нодлистом, если это так, то вы можете просто использовать метод длины непосредственно для него. Пример:

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

Ты можешь использовать Object.keys(data).length найти длину объекта JSON, имеющего ключевые данные

У Google Closure есть хорошая функция для этого... goog.object.getCount(obj)

посмотрите на goog. Объектная документация

Вы также можете разобрать Json, используя

JSON.parse(array).lenght

Я пытаюсь сделать его доступным для всех объектов следующим образом:

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

Я не думаю, что это возможно (по крайней мере, без использования некоторых внутренних). И я не думаю, что вы бы много выиграли, оптимизировав это.

Другие вопросы по тегам