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

1232

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

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

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

Показать ещё 1 комментарий
Теги:
key
performance
count
properties

19 ответов

2148
Лучший ответ

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

Object.keys(obj).length
  • 4
    если вы не возражаете против накладных расходов, все в порядке :)
  • 8
    Не только Node.js, но и любая среда, которая поддерживает ES5
Показать ещё 23 комментария
149

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

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;
  • 2
    Какова цель проверки (Object.prototype.hasOwnProperty.call(obj, k)) ?
  • 12
    @styfle Если вы используете цикл for для перебора свойств объекта, вы также получаете свойства в цепочке прототипов. Вот почему проверка hasOwnProperty необходима. Он возвращает только свойства, установленные для самого объекта.
Показать ещё 10 комментариев
122

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

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

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

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

  • 9
    Если вы используете underscore.js, вам следует использовать _.size. Хорошо, что если вы как-то переключаетесь с массива на объект или наоборот, результат остается прежним.
  • 0
    _.size хороший совет, я обновлю свой ответ.
Показать ещё 5 комментариев
66

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

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

1. ECMAScript Object.keys()

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

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

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

Многие примеры, основанные на библиотеке в другом месте этого раздела, являются полезными идиомами в контексте их библиотеки. С точки зрения производительности, однако, нет ничего, что можно было бы сравнить с совершенным кодом без библиотеки, поскольку все эти библиотечные методы фактически инкапсулируют либо объекты Object.keys -key for-loop, либо Object.keys (native или shimmed).

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

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

В противном случае ваш код может быть немного оптимизирован, сделав k local (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;

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

  • 0
    Почему бы var k in myobj повысить производительность? Насколько я знаю, только функции объявляют новую область видимости в JavaScript. Являются ли циклы исключением из этого правила?
  • 1
    Это быстрее? for (var k in myobj) hasOwn.call(myobj, k) && ++count; то есть замена оператора if на простой &&?
23

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

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

  • 4
    @hitautodestruct, потому что он предлагает решение.
  • 0
    @crush Этот ответ, кажется, подсказывает, что нужно делать, а не дает прямое решение.
Показать ещё 4 комментария
15

Как указано Avi Flax https://stackoverflow.com/questions/126100/how-to-efficiently-count-the-number-of-keys-properties-of-an-object-in-javascrip

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

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

15

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

  • 13
    Обратите внимание, что Object.prototype.__count__ удаляется в Gecko 1.9.3: whereswalden.com/2010/04/06/… count -property -of-objects-is-будучи удаленным /
  • 17
    Теперь, когда вышел Firefox 4, этот ответ устарел. Object.__count__ пропал, и хорошее избавление тоже.
Показать ещё 2 комментария
12

Итерация ответа 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);});}} */

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

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

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

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

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

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

_.size(obj)

  • 0
    В ответ на ваши комментарии о Array(obj).length : это не работает. http://jsfiddle.net/Jhy8M/
  • 0
    да, я посмотрел на это немного больше, я собираюсь в конечном итоге удалить этот ответ, если это возможно, или просто отредактировать все вместе
Показать ещё 2 комментария
8

Для тех, кто имеет Underscore.js, включенных в их проект, вы можете:

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

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

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

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

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

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

Вывод:

name:John Doe
email:[email protected]

Примечание: он не работает в < IE9.

5

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

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];
   }
}
  • 1
    Интересная альтернатива. Это устраняет накладные расходы функции статистического подсчета, но за счет вызова функции каждый раз, когда вы добавляете или удаляете элемент, что, к сожалению, может быть хуже. Я бы лично использовал такую реализацию списка для инкапсуляции данных и пользовательских методов, которые он может предоставить по сравнению с обычным массивом, но не тогда, когда мне просто нужен быстрый подсчет элементов.
  • 1
    Мне нравится ваш ответ, но я также лемминг и щелкнул upvote. Это представляет интересную дилемму. Вы не учитываете какое-либо поведение в своих инструкциях, например, мою ситуацию, когда я уже проголосовал против вашего ответа, но затем я получил указание «нажать upvote» и не могу. Инструкция молчаливо проваливается, но я понял из вашего контента на SO, что молчаливый сбой - это не то, что вам нравится в вашем коде. Просто один на один.
Показать ещё 7 комментариев
3

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

Ext.Object.getSize(myobj);

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

2

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

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

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

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

$(Object.Item).length
  • 1
    Кажется, что Object.Item не существует
1

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

  • 0
    Принятый ответ показывает, что это можно сделать, и у вас нет контекста, чтобы утверждать, что нечего получить.
0

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

0

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

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
0

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

-2

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню