Як ефективно підрахувати кількість ключів / властивостей об’єкта в 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.