Как эффективно посчитать количество ключей / свойств объекта в JavaScript? - PullRequest
1358 голосов
/ 24 сентября 2008

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

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

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

Ответы [ 20 ]

2247 голосов
/ 03 февраля 2011

Для этого в любой ES5-совместимой среде , такой как Узел , Chrome, IE 9+, Firefox 4+ или Safari 5 +:

Object.keys(obj).length
149 голосов
/ 15 апреля 2011

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

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;
133 голосов
/ 16 мая 2011

Если вы используете Underscore.js , вы можете использовать _. Size (спасибо @douwe):
_.size(obj)

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

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

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

72 голосов
/ 27 мая 2013

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

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

1. Object.keys ()

ECMAScript

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

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

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

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

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

Самая медленная часть такого цикла 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;

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

25 голосов
/ 24 сентября 2008

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

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

15 голосов
/ 17 января 2013

Как заявлено Ави Лен https://stackoverflow.com/a/4889658/1047014

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

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

15 голосов
/ 24 сентября 2008

Я не знаю ни одного способа сделать это, однако, чтобы свести итерации к минимуму, вы можете попробовать проверить наличие __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__ функция сработает и пересчитает.

12 голосов
/ 30 марта 2014

Для итерации по 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

_.size(obj)

8 голосов
/ 04 января 2012

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

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

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

_.size({a:'', b:''}) // => 2
6 голосов
/ 19 января 2018

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

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

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