Як ефективно підрахувати кількість ключів / властивостей об’єкта в JavaScript?


1544

Який найшвидший спосіб підрахувати кількість ключів / властивостей об’єкта? Чи можна це зробити без ітерації над об’єктом? тобто не роблячи

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

(Firefox надав магічну __count__властивість, але це було видалено десь у версії 4.)



2
показник ефективності для різних способів: jsben.ch/#/oSt2p
EscapeNetscape

1
Можливий дублікат довжини об’єкта JavaScript
Адам Кац

Відповіді:


2515

Для цього в будь -якому середовищі , сумісному з ES5 , наприклад, Node , Chrome, IE 9+, Firefox 4+ або Safari 5+:

Object.keys(obj).length

8
Не лише Node.js, але й будь-яке середовище, яке підтримує ES5
Yi Jiang

59
BTW ... щойно провів кілька тестів ... цей метод працює в O (n) час. Цикл для циклу не набагато гірший, ніж цей метод. ** сумне обличчя ** stackoverflow.com/questions/7956554 / ...
BMiner

161
-1 (-200, якщо я міг би) Це не тільки повторюється через об’єкт, але й створює цілий новий масив з усіма його ключами, тому він повністю не відповідає у відповіді на питання.
GetFree

42
Це здається набагато швидшим, ніж робити для (принаймні, на Chrome 25): jsperf.com/count-elements-in-object
fserb

34
@GetFree Чому стільки великих пальців? Це, безумовно, найшвидший спосіб з точки зору кодування. Не потрібні додаткові методи або бібліотеки. Щодо швидкості коду, очевидно, це теж не дуже погано. Зовсім не повний провал. 87 великих пальців не вдається для вас.
Андрій

150

Ви можете використовувати цей код:

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))?
стайф

14
@styfle Якщо ви використовуєте для циклу для перебору властивостей об'єкта, ви також можете отримати властивості в ланцюжку прототипів. Ось чому перевірка hasOwnPropertyнеобхідна. Він повертає лише властивості, встановлені на самому об'єкті.
Ренаат Де Мюнк

13
@styfle Щоб зробити це простіше, ви могли просто написати obj.hasOwnProperty(k)(я фактично це робив у своєму первісному дописі, але оновив його згодом). hasOwnPropertyдоступний на кожному об'єкті, тому що він є частиною Objectпрототипу 's, але в рідкісних випадках, коли цей метод буде видалено або перекрито, ви можете отримати несподівані результати. Зателефонувавши до нього, Object.prototypeце робить його трохи більш надійним. Причина використання callполягає в тому, що ви хочете викликати метод objзамість прототипу.
Ренаат Де Мюнк

6
Чи не було б краще використовувати цю версію? developer.mozilla.org/en-US/docs/JavaScript/Reference/…
Xavier Delamotte

1
@XavierDelamotte Ви абсолютно праві. Незважаючи на те, що моя версія працює, вона є дуже базовим і ментальним прикладом. Код Mozilla є більш безпечним. (PS: Ваше посилання також у прийнятій відповіді)
Renaat De Muynck

137

Якщо ви використовуєте Underscore.js, ви можете використовувати _.size (спасибі @douwe):
_.size(obj)

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

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

Інакше я підтримую відповідь @ Avi. Я відредагував це, щоб додати посилання на документ MDC, який включає метод ключів (), який ви можете додати до браузерів, які не входять до ECMA5.


9
Якщо ви використовуєте underscore.js, тоді вам слід використовувати _.size. Хороша річ у тому, що якщо ви якимось чином переключитесь з масиву на об’єкт або навпаки, результат залишається однаковим.
douwe

2
І з мого розуміння, лодаш, як правило, краще, ніж підкреслення (хоча вони роблять подібні речі).
Мерлін Морган-Грем

1
@ MerlynMorgan-Graham, якщо я пам'ятаю правильно, lodash спочатку є виделкою підкреслення ...
molson504x

6
_.keys(obj).lengthнайкраще працював для мене, тому що мій об'єкт повернення іноді - це звичайна рядок без властивостей всередині неї. _.size(obj)повертає мені довжину струни, а _.keys(obj).lengthповертає 0.
Jacob Stamm

O (n) складність . Лодаш і Підкреслення використовують Object.keysвнутрішньо. Підкреслення також копіює кожну клавішу в масив всередині for..inциклу, якщо Object.keysвін не визначений.
Н. Кудрявцев

82

Стандартна реалізація об'єкта ( ES5.1 Object Internal Properties and Methods ) не вимагає Objectвідстежувати його кількість ключів / властивостей, тому не повинно бути стандартного способу визначення розміру Objectбез чітко або неявно ітерації над його ключами.

Ось ось найчастіше використовувані альтернативи:

1. Object.keys ECMAScript ()

Object.keys(obj).length;Працює шляхом внутрішньої ітерації ключів для обчислення тимчасового масиву та повертає його довжину.

  • Плюси - читаний і чистий синтаксис. Ніяка бібліотека чи спеціальний код не потрібен, за винятком shim, якщо нативна підтримка не доступна
  • Мінуси - накладні витрати на пам'ять за рахунок створення масиву.

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;

Це швидше чи ні в даному середовищі - це питання бенчмаркінгу. У будь-якому випадку можна очікувати дуже обмеженого підвищення продуктивності.


Навіщо var k in myobjпідвищувати продуктивність? Наскільки мені відомо, лише функції оголошують новий діапазон у JavaScript. Чи є in-петлі виключенням із цього правила?
Ларс Гіруп Брінк Нільсен

1
Це швидше? for (var k in myobj) hasOwn.call(myobj, k) && ++count;тобто замінити оператор if на простий &&?
Гамза Кубба

Останнє, що ви можете зробити з Object.getOwnPropertyNames(obj).length:; набагато простіше.
Заповіт

29

Якщо ви насправді стикаєтеся з проблемою продуктивності, я б запропонував завершити виклики, які додають / видаляють властивості до / з об'єкта, функцією, яка також збільшує / зменшує властивість, що має належну назву (size?).

Вам потрібно лише один раз обчислити початкову кількість властивостей і рухатися далі. Якщо немає справжньої проблеми з роботою, не турбуйтеся. Просто ввімкніть цей фрагмент коду у функцію getNumberOfProperties(object)і виконайте його.


4
@hitautodestruct Тому що він пропонує рішення.
розчавити

@crush Ця відповідь, здається, пропонує скоріше зробити, а не дати пряме рішення.
hitautodestruct

5
@hitautodestruct пропонує відповідь: збільшення / зменшення інкапсульованого підрахунку методами додавання / видалення. Нижче є ще одна відповідь. Різниця лише в тому, що Confusion не запропонував жодного коду. Відповіді не зобов'язані надавати лише рішення коду.
розчав

1
це може бути не ідеально ... але порівняно з іншими "відповідями" це може бути найкращим рішенням для деяких ситуацій
d.raev

1
Поки що це єдине рішення, на який я бачу, що це O (1) постійна складність виконання часу, а тому є єдиним рішенням, яке відповідає на детальну запитання "без повторення" і, таким чином, має бути прийнятим відповіддю tru. Більшість, якщо не всі інші відповіді, не відповідають на це, оскільки вони пропонують O (n) лінійну складність виконання часу; це стосується також і рядкових рішень, які викликають щось на зразок функції .keys (), оскільки такі виклики функції є O (n).
cellepo

17

Як зазначає Avi Flax 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

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


1
Великі пальці, щоб згадати Object.getOwnPropertyNames, ви тут були єдиними ...
Wilt

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-
remo-off

17
Тепер, коли Firefox 4 вийшов, ця відповідь застаріла. Object.__count__пішло, і хорошої загадки теж.
Yi Jiang

1
Я б не сказав, що відповідь застаріла. Це все ще цікава стратегія інкапсулювати значення у функції.
devios1

слід використовувати об’єкт-прототип для розширення
Aaria Carter-Weir

13

Щоб повторити відповідь на 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)


У відповідь на ваші коментарі про Array(obj).length: Це не працює. http://jsfiddle.net/Jhy8M/
Джеймі Чонг

так, я поглянув на це трохи більше, якщо я, можливо, видаляю цю відповідь, якщо можливо, або просто редагуючи її всі разом
Belldandu

Я не бачу, що це має відношення до функцій, наприклад, і в Chrome я взагалі не бачу такої поведінки. Я б підозрював, що це могло мати відношення до поведінки Object.defineProperty () за замовчуванням : кількість, яка є false, хоча я ще не знайшов жодної документації про те, як вона var obj = { a: true, b: true }може відрізнятися від var obj = {}; obj.a = true; obj.b = true;або просто, якщо інша інтерпретація / семантика W3 має було прийнято Chrome.
Ноло

10

як відповів вище: Object.keys(obj).length

Але: як у нас зараз справжній клас Map у ES6, я б запропонував використовувати його замість властивостей об'єкта.

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

8

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

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

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

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

8

Ось кілька тестів на ефективність трьох методів;

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

Object.keys (). Довжина

20 735 операцій в секунду

Дуже простий і сумісний. Працює швидко, але дорого, тому що створює новий масив ключів, який потім викидається.

return Object.keys(objectToRead).length;

петлю через ключі

15 734 операції в секунду

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

Трохи повільніше, але ніде немає використання пам'яті, тому, напевно, краще, якщо ви зацікавлені в оптимізації для мобільних або інших маленьких машин

Використання карти замість об'єкта

953,839,338 операцій в секунду

return mapToRead.size;

В основному, Карта відстежує свій розмір, тому ми просто повертаємо числове поле. Далеко, набагато швидше, ніж будь-який інший метод. Якщо ви маєте контроль над об'єктом, перетворіть їх на карти.


6

Від: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Object.defineProperty (obj, prop, дескриптор)

Ви можете додати його до всіх своїх об'єктів:

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(var i in myObj) {
     console.log(i + ":" + myObj[i]);
}

Вихід:

name:John Doe
email:leaked@example.com

Примітка: вона не працює в <IE9 браузерах.


Якщо ви збираєтеся розширювати вбудовані прототипи або поліфікувати властивість (тобто мавпа-патч), будь ласка, зробіть це правильно: для сумісності вперед перевірте, чи існує властивість спочатку, а потім зробіть властивість нечисленною, щоб власні ключі споруджені об'єкти не забруднюються. Для методів використовують фактичні методи . Моя рекомендація: дотримуйтесь цих прикладів, які демонструють, як додати метод, який поводиться максимально близько, як вбудовані методи.
user4642212

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
Цікава альтернатива. Це виключає накладні витрати сукупної функції підрахунку, але ціною виклику функції кожен раз, коли ви додаєте або видаляєте елемент, що, на жаль, може бути гіршим. Я особисто використовував би таку реалізацію списку для інкапсуляції даних та спеціальних методів, які він може надати порівняно з простим масивом, але не тоді, коли мені просто потрібен швидкий підрахунок елементів.
Luc125

1
Мені подобається ваша відповідь, але я також є лемінг і натиснув нагороду. Це представляє цікаву дилему. Ви не враховуєте деяку поведінку в своїх інструкціях, наприклад, мою ситуацію, коли я вже відхилив вашу відповідь, але потім мені доручають "натиснути заявку" і не можу. Інструкція мовчки виходить з ладу, але я вибираю з вашого вмісту тут, так, що вийти з ладу мовчки - це не те, що вам подобається робити ваш код. Просто голови вгору.
L0j1k

1
Мені дуже подобається ця відповідь, приємна структура даних. І якби було вплив на продуктивність із викликом функції на додавання, було б набагато більше підвищення продуктивності, якщо доведеться перебирати об’єкт. Це повинно враховувати найшвидший цикл паттенvar i = basiclist.count while(i--){...}
Лекс

Чи не повинен базовий список хоча б включати базові чеки? Як і перевірка, чи addзамінює старий елемент чи removeвикликає його неіснуючим індексом. Також неможливо перевірити, чи є в списку заданий індекс, якщо undefinedце дійсне значення елемента.
Роберт

2
Список має бути упорядкований та перероблений. Дані зберігаються в об'єкті, тому немає гарантії на впорядкованість елементів. Як ви знаходите список довжини з отворами у ньому? this.count? Найвище значення індексу? Якщо ви коли-небудь додаєте два елементи в одному індексі, підрахунок переходить у стан помилки.
Остаточний укус

4

Ви можете використовувати Object.keys(data).lengthдовжину об'єкта JSON, що містить ключові дані


3

Для тих, хто має Ext JS 4 у своєму проекті, ви можете:

Ext.Object.getSize(myobj);

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


2

Ви можете використовувати:

Object.keys(objectName).length; 

і

Object.values(objectName).length;

1

OP не вказав, чи об'єкт nodeList, якщо він є, то ви можете просто використовувати метод довжини на ньому безпосередньо. Приклад:

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

0

Якщо jQuery вище не працює, спробуйте

$(Object.Item).length

3
Здається, що Object.Itemне існує
Luc125

-1

Я не думаю, що це можливо (принаймні, не без використання внутрішніх даних). І я не думаю, що ви багато чого заробляте, оптимізуючи це.


2
Прийнята відповідь показує, що це можна зробити, і у вас немає контексту, щоб стверджувати, що нічого виграти.
Провести

-1

Я намагаюся зробити його доступним для всіх таких об’єктів:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.