JavaScript'te bir nesnenin anahtar / özellik sayısını verimli bir şekilde nasıl sayabilirim?


1544

Bir nesnenin anahtar / özellik sayısını saymanın en hızlı yolu nedir? Bunu nesne üzerinde yinelemeden yapmak mümkün mü? yani yapmadan

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

(Firefox sihirli bir __count__özellik sağladı , ancak bu sürüm 4'ün etrafında bir yerde kaldırıldı.)



2
farklı yollar için bir performans ölçütü: jsben.ch/#/oSt2p
EscapeNetscape

Yanıtlar:


2516

Bunu Düğüm , Chrome, IE 9+, Firefox 4+ veya Safari 5+ gibi ES5 uyumlu herhangi bir ortamda yapmak için :

Object.keys(obj).length

8
Sadece Node.js değil, ES5'i destekleyen herhangi bir ortam
Yi Jiang

59
BTW ... sadece bazı testler yaptı ... bu yöntem O (n) zamanında çalışır. For for döngüsü bu yöntemden çok daha kötü değildir. ** üzgün yüz ** stackoverflow.com/questions/7956554/…
BMiner

161
-1 (-200 yapabilirsem) Bu sadece nesne üzerinden yinelenmez, aynı zamanda tüm anahtarları ile tamamen yeni bir dizi oluşturur, bu yüzden soruyu cevaplamada tamamen başarısız olur.
GetFree

42
Bunu yapmaktan çok daha hızlı görünüyor (en azından Chrome 25'te): jsperf.com/count-elements-in-object
fserb 18:12

34
@GetFree Neden bu kadar çok başparmak? Bu kesinlikle kodlama açısından en hızlı yoldur. Ek yöntem veya kitaplık gerekmez. Kod hızı açısından, görünüşe göre çok da kötü değil. Tam bir başarısızlık yok. 87 Yaşasın senin için başarısız.
Andrew

150

Bu kodu kullanabilirsiniz:

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

Ardından bunu eski tarayıcılarda da kullanabilirsiniz:

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

2
Çekin amacı nedir (Object.prototype.hasOwnProperty.call(obj, k))?
styfle

14
@styfle Nesnenin özellikleri üzerinde yineleme yapmak için bir for döngüsü kullanırsanız, özellikleri prototip zincirinde de alırsınız. Bu yüzden kontrol hasOwnPropertygerekli. Yalnızca nesnenin kendisinde ayarlanan özellikleri döndürür.
Renaat De Muynck

13
@styfle Daha basit hale getirmek için sadece yazabilirsiniz obj.hasOwnProperty(k)(bunu orijinal yazımda yaptım, ancak daha sonra güncelledim). 'nin prototipinin bir hasOwnPropertyparçası olduğu için her nesnede kullanılabilir Object, ancak nadiren de olsa bu yöntemin kaldırılması veya geçersiz kılınması beklenmedik sonuçlar alabilirsiniz. Onu arayarak Object.prototypebiraz daha sağlam hale getirir. Bunun nedeni , prototip yerine callyöntemi çağırmak istemenizdir obj.
Renaat De Muynck

6
Bu sürümü kullanmak daha iyi olmaz mıydı? developer.mozilla.org/tr-TR/docs/JavaScript/Reference/…
Xavier Delamotte

1
@XavierDelamotte Kesinlikle haklısın. Benim sürümüm çalışırken, örnek olarak çok temel ve ment. Mozilla'nın kodu daha güvenlidir. (PS: Bağlantınız da kabul edilen cevapta)
Renaat De Muynck

137

Eğer kullanıyorsanız Underscore.js kullanabileceğiniz _.size (teşekkür @douwe):
_.size(obj)

Alternatif olarak, bazıları için daha net olabilecek _.keys kullanabilirsiniz :
_.keys(obj).length

Underscore tavsiye ederim, onun çok temel şeyler yapmak için sıkı bir kütüphane. Mümkün olduğunda ECMA5 ile eşleşir ve yerel uygulamaya erteler.

Aksi takdirde @ Avi'nin cevabını destekliyorum. ECC5 olmayan tarayıcılara ekleyebileceğiniz keys () yöntemini içeren bir MDC dokümanı bağlantısı eklemek için düzenledim.


9
Underscore.js kullanıyorsanız, bunun yerine _.size kullanmalısınız. İyi bir şey, bir şekilde diziden nesneye veya tersi yönde geçiş yaparsanız sonucun aynı kalmasıdır.
douwe

2
Ve benim anlayışımdan lodash genellikle alt çizgiden daha iyidir (benzer şeyler yapsalar da).
Merlyn Morgan-Graham

1
@ MerlynMorgan-Graham doğru hatırlıyorsam, lodash aslında alt çizgi bir çatal ...
molson504x

6
_.keys(obj).lengthbenim için en iyi çalıştı, çünkü dönüş nesnem bazen içinde özellik olmayan düz bir dize. _.size(obj)bana dizenin uzunluğunu geri verir, _.keys(obj).length0 döndürür.
Jacob Stamm

O (n) karmaşıklığı . Lodash ve Alt çizgi Object.keysdahili olarak kullanılır. Alt çizgi de tanımlanmamışsa her anahtarı bir for..indöngü içindeki bir diziye kopyalar Object.keys.
N. Kudryavtsev

82

Standart Nesne uygulaması ( ES5.1 Nesne İç Özellikleri ve Yöntemleri ), Objectanahtar / özellik sayısını izlemek için a gerektirmez Object.

İşte en yaygın kullanılan alternatifler:

1. ECMAScript'in Object.keys ()

Object.keys(obj).length;Geçici bir dizi hesaplamak için tuşlar üzerinde dahili olarak yineleyerek çalışır ve uzunluğunu döndürür.

  • Artıları - Okunabilir ve temiz sözdizimi. Yerel destek yoksa bir dolgu dışında kütüphane veya özel kod gerekmez
  • Dezavantajları - Dizinin oluşturulması nedeniyle bellek ek yükü.

2. Kütüphane tabanlı çözümler

Bu konunun başka yerlerinde birçok kütüphane tabanlı örnek, kendi kütüphaneleri bağlamında yararlı deyimlerdir. Bununla birlikte, performans bakış açısından, mükemmel bir kütüphane olmayan kodla karşılaştırıldığında kazanılacak hiçbir şey yoktur, çünkü tüm bu kütüphane yöntemleri aslında bir for-loop veya ES5'i Object.keys(yerel veya shimmed) kapsamaktadır.

3. Bir for-loop'u optimize etme

Bu tür bir for- loop'un en yavaş kısmı.hasOwnProperty() , fonksiyon çağrısı yükü nedeniyle genellikle çağrıdır. Bu yüzden sadece bir JSON nesnesinin giriş sayısını istediğimde, .hasOwnProperty()hiçbir kodun ne kadar genişlemediğini bildiğimde aramayı atlıyorum Object.prototype.

Aksi takdirde, kodunuz klocal ( var k) yapılarak ++countve postfix yerine önek artış operatörü ( ) kullanılarak çok az optimize edilebilir .

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

Başka bir fikir, hasOwnPropertyyöntemin önbelleğe alınmasına dayanır :

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

Bunun belirli bir ortamda daha hızlı olup olmadığı bir kıyaslama meselesidir. Yine de çok sınırlı bir performans artışı beklenebilir.


var k in myobjPerformansı neden artıralım? Bildiğim kadarıyla, JavaScript'te yalnızca işlevler yeni kapsam bildiriyor. Döngüler bu kuralın bir istisnası mı?
Lars Gyrup Brink Nielsen

1
Bu daha hızlı mı? for (var k in myobj) hasOwn.call(myobj, k) && ++count;yani if ​​ifadesini basit bir &&?
Hamza Kubba

Yapabileceğiniz son şey Object.getOwnPropertyNames(obj).length:; çok daha basit.
Wilt

29

Gerçekte bir performans sorunu ile karşılaşıyorsanız, nesneye / nesneden özellikler ekleyen / kaldıran çağrıları, uygun şekilde adlandırılmış (boyut?) Bir özelliği de artıran / azaltan bir işlevle sarmanızı öneririm.

İlk özellik sayısını yalnızca bir kez hesaplamanız ve oradan devam etmeniz gerekir. Gerçek bir performans sorunu yoksa, rahatsız etmeyin. Sadece bir kod parçasını bir fonksiyona sarın getNumberOfProperties(object)ve onunla yapın.


4
@hitautodestruct Çünkü bir çözüm sunuyor.
Ezmek

@crush Bu yanıt, doğrudan bir çözüm vermek yerine yapılması gerekenleri önermektedir.
hitautodestruct

5
@hitautodestruct bir yanıt önerir: toplama / kaldırma yöntemleriyle kapsüllenmiş bir sayıyı artırma / azaltma. Tam olarak aşağıdaki gibi başka bir cevap var. Tek fark, Confusion herhangi bir kod sunmadı. Yanıtlar yalnızca kod çözümleri sağlamak için zorunlu değildir.
ezmek

1
mükemmel olmayabilir ... ama diğer "cevaplar" ile karşılaştırıldığında bu bazı durumlar için en iyi çözüm olabilir
d.raev

1
Şimdiye kadar gördüğüm tek çözüm, O (1) sabit zamanlı performans karmaşıklığı ve bu nedenle "yinelemeden" Sorusu ayrıntısına cevap veren ve bu nedenle gerçek Kabul Edilen Yanıt olması gereken tek çözüm. Diğer tüm cevaplar buna cevap vermiyorsa, çoğu O (n) doğrusal zaman performans karmaşıklığı sunar; bu, .keys () işlevi gibi bir şeyi çağıran 1 satırlı çözümler için de geçerlidir, çünkü bu işlev çağrıları O (n) şeklindedir.
cellepo

17

Avi Flax tarafından belirtildiği gibi https://stackoverflow.com/a/4889658/1047014

Object.keys(obj).length

nesnenizdeki tüm numaralandırılabilir özellikler için hile yapar ancak bunun yerine numaralandırılamayan özellikleri de dahil eder Object.getOwnPropertyNames. Fark şu:

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

Belirtildiği gibi burada bu aynı tarayıcı desteği vardırObject.keys

Bununla birlikte, çoğu durumda, bu tür işlemlere sayılamayanları dahil etmek istemeyebilirsiniz, ancak farkı bilmek her zaman iyidir;)


1
Bahsettiğim için Yaşasın Object.getOwnPropertyNames, burada sadece sen vardı ...
Wilt

15

Bunu yapmanın herhangi bir yolunun farkında değilim, ancak yinelemeleri minimumda tutmak için, varlığını kontrol etmeyi deneyebilirsiniz __count__ve yoksa (yani Firefox değil), nesne üzerinde yineleyebilir ve tanımlayabilirsiniz. daha sonra kullanmak için örneğin:

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

Bu şekilde destekleyici herhangi bir tarayıcı __count__bunu kullanır ve yinelemeler yalnızca kullanmayanlar için gerçekleştirilir. Sayı değişirse ve bunu yapamazsanız, bunu her zaman bir işlev haline getirebilirsiniz:

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

Bu şekilde myobj'e ne zaman başvurursanız. __count__fonksiyon patlayacak ve yeniden hesaplanacaktır.


13
Not Object.prototype.__count__Gecko 1.9.3 çıkarıldı olan: whereswalden.com/2010/04/06/... sayısı / -özelliği-of-nesneleri--kaldırılmakta olan
dshaw

17
Şimdi Firefox 4 çıktı, bu cevap artık kullanılmıyor. Object.__count__gitti ve iyi kurtuluş da oldu.
Yi Jiang

1
Cevabın eski olduğunu söyleyemem. Bir fonksiyondaki bir değeri kapsüllemek hala ilginç bir stratejidir.
devios1

uzatmak için prototip nesnesini kullanıyor olmalı
Aaria Carter-Weir

13

Avi Keten üzerinde yineleme yapmak için, Object.keys (obj) .length, kendisine bağlı işlevlere sahip olmayan bir nesne için doğrudur.

misal:

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

karşı

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

bundan kaçınmak için adımlar:

  1. içindeki anahtar sayısını saymak istediğiniz bir nesneye fonksiyon koymayın

  2. ayrı bir nesne kullanın veya özellikle işlevler için yeni bir nesne yapın (kullanarak dosyada kaç işlev olduğunu saymak istiyorsanız Object.keys(obj).length)

Ayrıca evet benim örnekte nodejs gelen _ veya alt çizgi modülü kullanılır

belgelere http://underscorejs.org/ adresinin yanı sıra github ve diğer çeşitli bilgilerdeki kaynaklardan ulaşabilirsiniz.

Ve nihayet bir lodash uygulaması https://lodash.com/docs#size

_.size(obj)


Hakkındaki yorumlarınıza yanıt olarak Array(obj).length: Çalışmıyor. http://jsfiddle.net/Jhy8M/
Jamie Chong

evet ben biraz daha im im mümkünse bu cevabı kaldırma veya sadece birlikte düzenleme sona erecek
Belldandu

Bunun işlevlerle bir ilgisi olduğunu görmüyorum, biri için ve Chrome'da bu davranışı hiç görmüyorum. Bu varsayılan davranışı ile yapmak zorunda olduğundan şüphe ediyorum enumerable: Object.defineProperty () olan falsehenüz konusunda herhangi bir belge bulunamadı ettik olsa var obj = { a: true, b: true }farklı olabilir var obj = {}; obj.a = true; obj.b = true;veya W3 farklı bir yorumlama / semantik vardır basitçe eğer Chrome tarafından kabul edildi.
Nolo

10

yukarıda cevaplandığı gibi: Object.keys(obj).length

Ancak: şimdi ES6'da gerçek bir Map sınıfımız olduğu için, bir nesnenin özelliklerini kullanmak yerine bunu kullanmanızı öneririm .

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

8

Underscore.js projelerine dahil olanlar için şunları yapabilirsiniz:

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

veya fonksiyonel stil:

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

8

İşte üç yöntem için bazı performans testleri;

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

Object.keys (). Uzunlukta

Saniyede 20.735 işlem

Çok basit ve uyumlu. Hızlı ama pahalı bir şekilde çalışır çünkü yeni bir anahtar dizisi oluşturur, bu da daha sonra atılır.

return Object.keys(objectToRead).length;

anahtarlar arasında dolaşmak

Saniyede 15.734 işlem

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

Biraz daha yavaş, ancak bellek kullanımına yakın bir yer yok, bu yüzden mobil veya diğer küçük makineler için optimizasyon yapmak istiyorsanız daha iyi

Nesne yerine Harita Kullanma

Saniyede 953.839.338 işlem

return mapToRead.size;

Temel olarak, Harita kendi boyutunu izler, böylece sadece bir sayı alanı döndürürüz. Diğer yöntemlerden çok daha hızlı. Nesne üzerinde kontrolünüz varsa, bunları haritalara dönüştürün.


6

Gönderen: https://developer.mozilla.org/tr/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Object.defineProperty (obj, prop, tanımlayıcı)

Tüm nesnelerinize ekleyebilirsiniz:

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

Veya tek bir nesne:

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

Misal:

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

Bu şekilde eklendi, for döngülerinde görüntülenmeyecek:

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

Çıktı:

name:John Doe
email:leaked@example.com

Not: <IE9 tarayıcılarında çalışmaz.


Yerleşik prototipleri genişletecek veya bir özelliği (örneğin maymun yaması) çoklu dolduracaksanız, lütfen doğru şekilde yapın: ileri uyumluluk için, özelliğin önce var olup olmadığını kontrol edin, ardından özelliği kendi anahtarlarının numaralandırılamaz hale getirin inşa edilen nesnelerin sayısı kirlenmez. Yöntemleri için kullanmak fiili yöntemler . Benim tavsiyem: Yerleşik yöntemler gibi olabildiğince yakın davranan bir yöntemin nasıl ekleneceğini gösteren bu örnekleri izleyin .
user4642212

5

Bu sorunu nasıl çözdüğüm, nesnede kaç öğe saklandığının kaydını tutan temel bir listeyi kendi uygulamam oluşturmaktır. Çok basit. Bunun gibi bir şey:

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
İlginç bir alternatif. Bu, toplam sayım işlevinin ek yükünü ortadan kaldırır, ancak her öğe eklediğinizde veya kaldırdığınızda bir işlev çağrısı pahasına, ne yazık ki daha kötü olabilir. Şahsen veri kapsülleme ve düz bir dizi ile karşılaştırıldığında sağlayabilir özel yöntemler için böyle bir liste uygulaması kullanmak istiyorum, ama ben sadece hızlı öğe sayma gerektiğinde değil.
Luc125

1
Cevabınızı seviyorum, ama aynı zamanda bir lemming ve tıklanmış upvote'um. Bu ilginç bir ikilem sunuyor. Talimatlarınızda, cevabınızı zaten iptal ettiğim durumum gibi bazı davranışları hesaba katmıyorsunuz, ancak daha sonra "upvote'u tıklamanız" isteniyor ve yapamıyorum. Talimat sessizce başarısız olur, ancak SO'daki içeriğinizden toplanırım, sessizce başarısız olmanız, kodunuzun yapılmasını sevdiğiniz bir şey değildir. Sadece aklınızda bulunsun.
L0j1k

1
Bu cevabı çok seviyorum, güzel veri yapısı. Ve add işlev çağrısında bir performans etkisi olsaydı, bir nesne üzerinde yineleme yapmak zorunda kalırsanız çok daha yüksek bir performans artışı olur. Bu, en hızlı döngü pattenine izin vermelidirvar i = basiclist.count while(i--){...}
Lex

Temel bir liste en azından temel kontrolleri içermemeli midir? addEski bir öğeyi değiştirip değiştirmediğini veya removevar olmayan bir dizinle çağrılıp çağrılmadığını kontrol etmek gibi . Ayrıca undefined, geçerli bir öğe değeri ise listenin belirli bir dizini olup olmadığını kontrol etmek de mümkün değildir .
Robert

2
Bir liste sipariş edilmeli ve yinelenebilir olmalıdır. Veriler bir nesnede saklanır, bu nedenle öğelerin sıralanması konusunda hiçbir garanti yoktur. İçinde delik bulunan bir listeyi nasıl bulabilirsin? this.count? En yüksek dizin değeri? Aynı dizine iki öğe eklerseniz sayım hata durumuna geçer.
Ultimate Gobblement

4

Object.keys(data).lengthAnahtar verilere sahip JSON nesnesinin uzunluğunu bulmak için kullanabilirsiniz


3

Projelerinde Ext JS 4 olanlar için şunları yapabilirsiniz:

Ext.Object.getSize(myobj);

Bunun avantajı, tüm Ext uyumlu tarayıcılarda (IE6-IE8 dahil) çalışacağıdır, ancak diğer önerilen çözümlerde olduğu gibi çalışma süresinin O (n) 'den daha iyi olmadığına inanıyorum.


2

Kullanabilirsiniz:

Object.keys(objectName).length; 

ve

Object.values(objectName).length;

1

OP nesnenin bir nodeList olup olmadığını belirtmedi, eğer öyleyse doğrudan uzunluk yöntemini kullanabilirsiniz . Misal:

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


-1

Bunun mümkün olduğunu düşünmüyorum (en azından bazı içselleri kullanmadan). Ve bunu optimize ederek fazla kazanacağınızı sanmıyorum.


2
Kabul cevap gösterileri bu o olabilir yapılabilir ve kazanç şey olmadığını assert hiçbir bağlama sahip.
Boru

-1

Ben böyle tüm nesneler için kullanılabilir hale getirmeye çalışın:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.