Bagaimana cara menghitung jumlah kunci / properti suatu objek dalam JavaScript secara efisien?

1545

Apa cara tercepat untuk menghitung jumlah kunci / properti suatu objek? Mungkinkah melakukan ini tanpa mengulangi objek? yaitu tanpa melakukan

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

(Firefox memang memberikan __count__properti sulap , tetapi ini dihapus di suatu tempat di sekitar versi 4.)

mjs
sumber
2
tolok ukur kinerja untuk berbagai cara: jsben.ch/#/oSt2p
EscapeNetscape
1
Kemungkinan rangkap dari Panjang objek JavaScript
Adam Katz

Jawaban:

2518

Untuk melakukan ini di lingkungan yang kompatibel dengan ES5 , seperti Node , Chrome, IE 9+, Firefox 4+, atau Safari 5+:

Object.keys(obj).length
Avi Flax
sumber
8
Tidak hanya Node.js, tetapi lingkungan apa pun yang mendukung ES5
Yi Jiang
59
BTW ... baru saja menjalankan beberapa tes ... metode ini berjalan dalam waktu O (n). A for loop tidak jauh lebih buruk daripada metode ini. ** wajah sedih ** stackoverflow.com/questions/7956554/...
BMiner
161
-1 (-200 jika saya bisa) Ini tidak hanya beralih melalui objek tetapi juga menciptakan array baru dengan semua kunci, sehingga benar-benar gagal menjawab pertanyaan.
GetFree
42
Tampaknya jauh lebih cepat daripada melakukan untuk (setidaknya di Chrome 25): jsperf.com/count-elements-in-object
fserb
34
@GETFree Mengapa begitu banyak acungan jempol? Ini jelas merupakan cara tercepat dalam hal pengkodean. Tidak diperlukan metode atau pustaka tambahan. Dalam hal kecepatan kode, rupanya juga tidak terlalu buruk. Sama sekali tidak gagal sama sekali. 87 jempol gagal untuk Anda.
Andrew
150

Anda bisa menggunakan kode ini:

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

Kemudian Anda dapat menggunakan ini di browser lama juga:

var len = Object.keys(obj).length;
Renaat De Muynck
sumber
2
Apa tujuan dari cek itu (Object.prototype.hasOwnProperty.call(obj, k))?
styfle
14
@styfle Jika Anda menggunakan untuk loop untuk iterate atas properti objek, Anda juga mendapatkan properti dalam rantai prototipe. Karena itu hasOwnPropertyperlu dilakukan pengecekan . Ini hanya mengembalikan properti yang ditetapkan pada objek itu sendiri.
Renaat De Muynck
13
@styfle Untuk membuatnya lebih sederhana, Anda bisa menulis obj.hasOwnProperty(k)(saya benar-benar melakukan ini di posting asli saya, tetapi memperbaruinya nanti). hasOwnPropertytersedia pada setiap objek karena itu adalah bagian dari Objectprototipe, tetapi dalam kejadian langka bahwa metode ini akan dihapus atau diganti Anda mungkin mendapatkan hasil yang tidak terduga. Dengan memanggilnya, Object.prototypeitu membuatnya sedikit lebih kuat. Alasan penggunaannya calladalah karena Anda ingin menjalankan metode objalih-alih pada prototipe.
Renaat De Muynck
6
Bukankah lebih baik menggunakan versi ini? developer.mozilla.org/en-US/docs/JavaScript/Reference/…
Xavier Delamotte
1
@XavierDelamotte Anda benar sekali. Sementara versi saya berfungsi, ini sangat mendasar dan sebagai contoh. Kode Mozilla lebih aman. (PS: Tautan Anda juga ada dalam jawaban yang diterima)
Renaat De Muynck
137

Jika Anda menggunakan Underscore.js, Anda dapat menggunakan _.size (terima kasih @douwe):
_.size(obj)

Atau Anda juga dapat menggunakan _.keys yang mungkin lebih jelas untuk beberapa:
_.keys(obj).length

Saya sangat merekomendasikan Underscore, ini adalah perpustakaan yang ketat untuk melakukan banyak hal dasar. Kapan saja mereka cocok dengan ECMA5 dan tunduk pada implementasi asli.

Kalau tidak, saya mendukung jawaban @ Avi. Saya mengeditnya untuk menambahkan tautan ke dokumen MDC yang mencakup metode keys () yang dapat Anda tambahkan ke browser non-ECMA5.

studgeek
sumber
9
Jika Anda menggunakan underscore.js maka Anda harus menggunakan _.size sebagai gantinya. Hal yang baik adalah bahwa jika Anda entah bagaimana beralih dari array ke objek atau sebaliknya hasilnya tetap sama.
douwe
2
Dan dari pengertian saya, lodash umumnya lebih baik daripada garis bawah (meskipun mereka melakukan hal serupa).
Merlyn Morgan-Graham
1
@ MerlynMorgan-Graham jika saya ingat dengan benar, lodash awalnya adalah garpu garis bawah ...
molson504x
6
_.keys(obj).lengthbekerja paling baik untuk saya, karena objek pengembalian saya kadang-kadang string polos tanpa properti di dalamnya. _.size(obj)memberi saya kembali panjang string, sementara _.keys(obj).lengthmengembalikan 0.
Jacob Stamm
O (n) kompleksitas . Lodash dan Underscore digunakan secara Object.keysinternal. Garis bawah juga menyalin setiap kunci ke dalam array di dalam for..inloop jika Object.keystidak ditentukan.
N. Kudryavtsev
82

Implementasi Objek standar ( ES5.1 Objek Properti dan Metode Internal ) tidak memerlukan Objectuntuk melacak jumlah kunci / properti, sehingga tidak boleh ada cara standar untuk menentukan ukuran Objecttanpa berulang-ulang atau tersirat secara implisit atas kunci-kuncinya.

Jadi di sini adalah alternatif yang paling umum digunakan:

1. ECMAScript's Object.keys ()

Object.keys(obj).length;Bekerja dengan pengulangan internal atas kunci untuk menghitung array sementara dan mengembalikan panjangnya.

  • Pro - Sintaks yang mudah dibaca dan bersih. Tidak perlu pustaka atau kode khusus kecuali shim jika dukungan asli tidak tersedia
  • Kontra - Memori overhead karena penciptaan array.

2. Solusi berbasis perpustakaan

Banyak contoh berbasis perpustakaan di tempat lain dalam topik ini adalah idiom yang berguna dalam konteks perpustakaan mereka. Dari sudut pandang kinerja, bagaimanapun, tidak ada untungnya dibandingkan dengan kode tanpa pustaka yang sempurna karena semua metode pustaka tersebut benar-benar merangkum for-loop atau ES5 Object.keys(asli atau shimmed).

3. Mengoptimalkan for-loop

Bagian paling lambat dari for-loop umumnya adalah .hasOwnProperty()panggilan, karena fungsi panggilan overhead. Jadi ketika saya hanya ingin jumlah entri objek JSON, saya hanya melewatkan .hasOwnProperty()panggilan jika saya tahu bahwa tidak ada kode yang juga tidak akan diperpanjang Object.prototype.

Kalau tidak, kode Anda bisa sangat sedikit dioptimalkan dengan membuat klocal ( var k) dan dengan menggunakan pre-increment operator ( ++count) alih-alih postfix.

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

Gagasan lain bergantung pada caching hasOwnPropertymetode:

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

Apakah ini lebih cepat atau tidak pada lingkungan tertentu adalah masalah pembandingan. Bagaimanapun juga, perolehan kinerja yang sangat terbatas dapat diharapkan.

Luc125
sumber
Mengapa harus var k in myobjmeningkatkan kinerja? Sejauh yang saya tahu, hanya fungsi yang menyatakan ruang lingkup baru dalam JavaScript. Apakah in-loop merupakan pengecualian terhadap aturan ini?
Lars Gyrup Brink Nielsen
1
Apakah ini lebih cepat? for (var k in myobj) hasOwn.call(myobj, k) && ++count;yaitu mengganti pernyataan if dengan sederhana &&?
Hamza Kubba
Hal terakhir yang dapat Anda lakukan dengan Object.getOwnPropertyNames(obj).length:; jauh lebih sederhana.
Layu
29

Jika Anda benar-benar mengalami masalah kinerja, saya sarankan membungkus panggilan yang menambah / menghapus properti ke / dari objek dengan fungsi yang juga menambah / mengurangi properti (ukuran?) Yang dinamai dengan tepat.

Anda hanya perlu menghitung jumlah awal properti sekali dan beralih dari sana. Jika tidak ada masalah kinerja aktual, jangan repot-repot. Hanya bungkus sedikit kode itu dalam suatu fungsi getNumberOfProperties(object)dan selesai dengan itu.

Kebingungan
sumber
4
@hitautodestruct Karena dia menawarkan solusi.
naksir
@ Crush Jawaban ini tampaknya menyarankan hal-hal yang harus dilakukan daripada memberikan solusi langsung.
hitautodestruct
5
@hitautodestruct menyarankan jawaban: menambah / mengurangi jumlah yang dienkapsulasi dengan metode add / remove. Ada jawaban lain persis seperti di bawah ini. Satu-satunya perbedaan adalah, Kebingungan tidak menawarkan kode apa pun. Jawaban tidak dimandatkan untuk memberikan solusi kode saja.
naksir
1
mungkin tidak sempurna ... tetapi dibandingkan dengan "jawaban" lainnya, ini mungkin solusi terbaik untuk beberapa situasi
d.raev
1
Sejauh ini, ini adalah satu-satunya solusi yang saya lihat yaitu O (1) kompleksitas kinerja waktu yang konstan, dan oleh karena itu merupakan satu-satunya solusi yang Menjawab perincian Pertanyaan "tanpa iterasi" dan oleh karena itu harus menjadi tru. Kebanyakan jika tidak semua jawaban lain tidak menjawab itu, karena mereka menawarkan O (n) kompleksitas kinerja waktu linier; itu yang terjadi juga untuk solusi 1-line yang memanggil sesuatu seperti fungsi .keys (), karena panggilan fungsi tersebut adalah O (n).
cellepo
17

Seperti yang dinyatakan oleh Avi Flax https://stackoverflow.com/a/4889658/1047014

Object.keys(obj).length

akan melakukan trik untuk semua properti enumerable pada objek Anda tetapi untuk juga menyertakan properti non-enumerable yang Anda dapat gunakan Object.getOwnPropertyNames. Inilah perbedaannya:

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

Seperti yang dinyatakan di sini ini memiliki dukungan browser yang sama denganObject.keys

Namun, dalam sebagian besar kasus, Anda mungkin tidak ingin memasukkan yang tidak dapat dihitung dalam jenis operasi ini, tetapi selalu baik untuk mengetahui perbedaannya;)

BenderTheOffender
sumber
1
Acungan jempol untuk menyebutkan Object.getOwnPropertyNames, Anda adalah satu-satunya di sini ...
Layu
15

Saya tidak mengetahui cara apa pun untuk melakukan ini, namun untuk menjaga iterasi seminimal mungkin, Anda dapat mencoba memeriksa keberadaan __count__dan jika tidak ada (mis. Bukan Firefox) maka Anda bisa beralih pada objek dan menentukan itu untuk digunakan nanti misalnya:

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

Dengan cara ini setiap pendukung browser __count__akan menggunakannya, dan iterasi hanya akan dilakukan untuk mereka yang tidak. Jika jumlah berubah dan Anda tidak bisa melakukan ini, Anda selalu bisa membuatnya berfungsi:

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

Dengan cara ini, kapan saja Anda merujuk myobj. __count__fungsi akan diaktifkan dan dihitung ulang.

Luke Bennett
sumber
13
Catatan yang Object.prototype.__count__sedang dihapus di Gecko 1.9.3: whereswalden.com/2010/04/06/… count -property-of-objects-is-being-
Remove
17
Sekarang setelah Firefox 4 keluar, jawaban ini sekarang sudah usang. Object.__count__sudah pergi, dan riddance baik juga.
Yi Jiang
1
Saya tidak akan mengatakan jawabannya sudah usang. Ini masih merupakan strategi yang menarik untuk merangkum nilai dalam suatu fungsi.
devios1
harus menggunakan objek prototipe untuk memperpanjang
Aaria Carter-Weir
13

Untuk beralih pada Avi Flax, jawab Object.keys (obj). Panjang sudah benar untuk objek yang tidak memiliki fungsi yang terkait dengannya

contoh:

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

melawan

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

langkah-langkah untuk menghindari ini:

  1. jangan letakkan fungsi di objek yang ingin Anda hitung jumlah kunci

  2. menggunakan objek terpisah atau membuat objek baru khusus untuk fungsi (jika Anda ingin menghitung berapa banyak fungsi yang ada dalam file menggunakan Object.keys(obj).length)

juga ya saya menggunakan modul _ atau garis bawah dari nodejs dalam contoh saya

dokumentasi dapat ditemukan di sini http://underscorejs.org/ serta sumbernya di github dan berbagai info lainnya

Dan akhirnya implementasi lodash https://lodash.com/docs#size

_.size(obj)

Belldandu
sumber
Menanggapi komentar Anda tentang Array(obj).length: Tidak berfungsi. http://jsfiddle.net/Jhy8M/
Jamie Chong
ya saya melihat ke dalamnya sedikit lebih im akhirnya akan menghapus jawaban ini jika mungkin atau hanya mengeditnya bersama
Belldandu
Saya tidak melihat bahwa ini ada hubungannya dengan fungsi, untuk satu, dan di Chrome saya tidak melihat perilaku ini sama sekali. Saya menduga ini mungkin ada hubungannya dengan perilaku default Object.defineProperty (): enumerable yang false, meskipun saya belum menemukan dokumentasi tentang bagaimana var obj = { a: true, b: true }mungkin berbeda dari var obj = {}; obj.a = true; obj.b = true;atau hanya jika interpretasi / semantik yang berbeda dari W3 memiliki telah diadopsi oleh Chrome.
Nolo
10

seperti yang dijawab di atas: Object.keys(obj).length

Tapi: seperti yang kita miliki sekarang kelas Peta nyata di ES6, saya akan menyarankan untuk menggunakannya daripada menggunakan properti dari suatu objek.

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

Bagi mereka yang memiliki Underscore.js termasuk dalam proyek mereka yang dapat Anda lakukan:

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

atau gaya fungsional:

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

Berikut adalah beberapa tes kinerja untuk tiga metode;

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

Panjang Object.keys ()

20.735 operasi per detik

Sangat sederhana dan kompatibel. Berjalan cepat tetapi mahal karena itu menciptakan array kunci baru, yang kemudian dibuang.

return Object.keys(objectToRead).length;

loop melalui tombol

15.734 operasi per detik

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

Sedikit lebih lambat, tetapi jauh dari penggunaan memori, jadi mungkin lebih baik jika Anda tertarik untuk mengoptimalkan untuk ponsel atau mesin kecil lainnya

Menggunakan Map, bukan Object

953.839.338 operasi per detik

return mapToRead.size;

Pada dasarnya, Peta melacak ukurannya sendiri sehingga kami hanya mengembalikan bidang angka. Jauh, jauh lebih cepat daripada metode lainnya. Jika Anda memiliki kendali atas objek, konversikan mereka ke peta.

Steve Cooper
sumber
6

Dari: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Object.defineProperty (obj, prop, deskriptor)

Anda bisa menambahkannya ke semua objek Anda:

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

Atau satu objek:

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

Contoh:

var myObj = {};
myObj.name  = "John Doe";
myObj.email = "[email protected]";
myObj.length; //output: 2

Ditambahkan dengan cara itu, itu tidak akan ditampilkan dalam for..in loop:

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

Keluaran:

name:John Doe
email:leaked@example.com

Catatan: itu tidak berfungsi di <IE9 browser.

lepe
sumber
Jika Anda akan memperpanjang prototipe bawaan atau polyfill sebuah properti (mis. Monkey-patch), silakan lakukan dengan benar: untuk meneruskan kompatibilitas, periksa apakah properti itu ada terlebih dahulu, kemudian buat properti itu tidak dapat dihitung sehingga kuncinya sendiri benda yang dibangun tidak tercemar. Untuk metode gunakan metode aktual . Rekomendasi saya: ikuti contoh-contoh ini yang menunjukkan bagaimana cara menambahkan metode yang berperilaku semirip mungkin dengan metode bawaan.
user4642212
5

Bagaimana saya memecahkan masalah ini adalah membangun implementasi saya sendiri dari daftar dasar yang menyimpan catatan tentang berapa banyak item yang disimpan dalam objek. Sangat sederhana. Sesuatu seperti ini:

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];
   }
}
Klik Upvote
sumber
1
Alternatif yang menarik. Ini tidak menghilangkan overhead dari fungsi penghitungan agregat, tetapi dengan biaya panggilan fungsi setiap kali Anda menambah atau menghapus elemen, yang sayangnya mungkin lebih buruk. Saya pribadi akan menggunakan implementasi daftar seperti itu untuk enkapsulasi data dan metode kustom yang dapat disediakan dibandingkan dengan array biasa, tetapi tidak ketika saya hanya perlu menghitung item cepat.
Luc125
1
Saya suka jawaban Anda, tetapi saya juga seorang lemming dan mengklik upvote. Ini menghadirkan dilema yang menarik. Anda tidak memperhitungkan beberapa perilaku dalam instruksi Anda, seperti situasi saya di mana saya sudah memilih jawaban Anda, tetapi kemudian saya diperintahkan untuk "klik upvote" dan tidak bisa. Instruksi gagal diam-diam tetapi saya mengumpulkan dari konten Anda di sini di SO bahwa gagal diam-diam bukanlah sesuatu yang Anda sukai kode Anda lakukan. Hanya kepala.
L0j1k
1
Saya sangat suka jawaban ini, struktur data yang bagus. Dan jika ada dampak kinerja dengan panggilan fungsi add, akan ada peningkatan kinerja yang jauh lebih besar jika harus beralih pada objek. Ini akan memungkinkan untuk patten loop tercepatvar i = basiclist.count while(i--){...}
Lex
Bukankah seharusnya daftar dasar paling tidak mencakup pemeriksaan dasar? Seperti memeriksa apakah addmengganti item lama atau jika removedisebut dengan indeks yang tidak ada. Juga tidak mungkin untuk memeriksa apakah daftar memiliki indeks yang diberikan jika undefinedmerupakan nilai item yang valid.
Robert
2
Daftar harus dipesan dan diulang. Data disimpan dalam suatu objek sehingga tidak ada jaminan pada pemesanan elemen. Bagaimana Anda menemukan daftar panjang dengan lubang di dalamnya? jumlah ini? Nilai indeks tertinggi? Jika Anda pernah menambahkan dua item pada indeks yang sama, penghitungannya berubah menjadi kesalahan.
Ultimate Gobblement
4

Anda dapat menggunakan Object.keys(data).lengthuntuk menemukan panjang objek JSON yang memiliki data kunci

Pembuat kode
sumber
3

Bagi mereka yang memiliki Ext JS 4 di proyek mereka yang dapat Anda lakukan:

Ext.Object.getSize(myobj);

Keuntungan dari ini adalah bahwa ia akan bekerja pada semua browser yang kompatibel dengan Ext (termasuk IE6-IE8), namun, saya percaya waktu yang berjalan tidak lebih baik daripada O (n), seperti solusi yang disarankan lainnya.

Mark Rhodes
sumber
2

Kamu bisa menggunakan:

Object.keys(objectName).length; 

dan

Object.values(objectName).length;
Fayaz
sumber
1

OP tidak menentukan apakah objek tersebut adalah nodeList, jika ya maka Anda bisa langsung menggunakan metode panjang . Contoh:

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

Jika jQuery di atas tidak berfungsi, maka coba

$(Object.Item).length
kode kode
sumber
3
Tampaknya Object.Itemtidak ada
Luc125
-1

Saya tidak berpikir ini mungkin (setidaknya tidak tanpa menggunakan beberapa internal). Dan saya tidak berpikir Anda akan mendapatkan banyak dengan mengoptimalkan ini.

sebuah campuran
sumber
2
Jawaban yang diterima menunjukkan bahwa ini dapat dilakukan, dan Anda tidak memiliki konteks untuk menyatakan bahwa tidak ada yang dapat diperoleh.
Saluran
-1

Saya mencoba membuatnya tersedia untuk semua objek seperti ini:

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
Taquatech
sumber