EDIT : Semua contoh di bawah dalam jawaban ini telah diedit untuk menyertakan variabel jalur baru yang dihasilkan dari iterator sesuai permintaan @ supersan . Variabel path adalah array string di mana setiap string dalam array mewakili setiap kunci yang diakses untuk mendapatkan nilai iterasi yang dihasilkan dari objek sumber asli. Variabel path dapat dimasukkan ke dalam get function / method lodash . Atau Anda dapat menulis versi get lodash Anda sendiri yang hanya menangani array seperti:
function get (object, path) {
return path.reduce((obj, pathItem) => obj ? obj[pathItem] : undefined, object);
}
const example = {a: [1,2,3], b: 4, c: { d: ["foo"] }};
// these paths exist on the object
console.log(get(example, ["a", "0"]));
console.log(get(example, ["c", "d", "0"]));
console.log(get(example, ["b"]));
// these paths do not exist on the object
console.log(get(example, ["e", "f", "g"]));
console.log(get(example, ["b", "f", "g"]));
EDIT : Jawaban yang diedit ini memecahkan traversal infinite looping.
Menghentikan Pesky Infinite Object Traversals
Jawaban yang diedit ini masih memberikan salah satu manfaat tambahan dari jawaban asli saya yang memungkinkan Anda untuk menggunakan fungsi generator yang disediakan untuk menggunakan antarmuka yang lebih bersih dan sederhana yang dapat diubah (pikirkan menggunakan for of
loop seperti di for(var a of b)
mana b
iterable dan a
merupakan elemen iterable ). Dengan menggunakan fungsi generator dan menjadi api yang lebih sederhana, ia juga membantu penggunaan kembali kode dengan membuatnya jadi Anda tidak perlu mengulangi logika iterasi di mana pun Anda ingin mengulangi secara mendalam pada properti objek dan juga memungkinkan untuk break
keluar dari loop jika Anda ingin menghentikan iterasi lebih awal.
Satu hal yang saya perhatikan yang belum diatasi dan yang tidak ada dalam jawaban asli saya adalah bahwa Anda harus berhati-hati melintasi objek yang sewenang-wenang (yaitu set "acak"), karena objek JavaScript dapat merujuk sendiri. Ini menciptakan peluang untuk memiliki traverse looping tanpa batas. Namun data JSON yang tidak dimodifikasi tidak dapat menjadi referensi sendiri, jadi jika Anda menggunakan subset objek JS ini, Anda tidak perlu khawatir tentang perulangan perulangan tak terbatas dan Anda dapat merujuk ke jawaban asli saya atau jawaban lain. Berikut ini adalah contoh dari traversal tanpa akhir (perhatikan itu bukan sepotong kode yang bisa dijalankan, karena jika tidak maka akan crash tab browser Anda).
Juga dalam objek generator dalam contoh saya yang diedit saya memilih untuk menggunakan Object.keys
alih-alih for in
hanya kunci non-prototipe pada objek. Anda dapat menukar ini sendiri jika Anda ingin kunci prototipe disertakan. Lihat bagian jawaban asli saya di bawah ini untuk implementasi dengan Object.keys
dan for in
.
Lebih buruk - Ini akan infinite loop pada objek referensial diri:
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
// this self-referential property assignment is the only edited line
// from the below original example which makes the traversal
// non-terminating (i.e. it makes it infinite loop)
o.o = o;
function* traverse(o, path=[]) {
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* traverse(o[I], itemPath);
}
}
}
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Untuk menyelamatkan diri Anda dari ini, Anda dapat menambahkan satu set dalam penutupan, sehingga ketika fungsi pertama kali dipanggil itu mulai membangun memori objek yang telah dilihat dan tidak melanjutkan iterasi setelah menemukan objek yang sudah terlihat. Cuplikan kode di bawah melakukan hal itu dan dengan demikian menangani kasus pengulangan yang tak terbatas.
Lebih baik - Ini tidak akan infinite loop pada objek referensial diri:
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
// this self-referential property assignment is the only edited line
// from the below original example which makes more naive traversals
// non-terminating (i.e. it makes it infinite loop)
o.o = o;
function* traverse(o) {
const memory = new Set();
function * innerTraversal (o, path=[]) {
if(memory.has(o)) {
// we've seen this object before don't iterate it
return;
}
// add the new object to our memory.
memory.add(o);
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* innerTraversal(o[i], itemPath);
}
}
}
yield* innerTraversal(o);
}
console.log(o);
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Jawaban Asli
Untuk cara yang lebih baru untuk melakukannya jika Anda tidak keberatan menjatuhkan IE dan terutama mendukung lebih banyak browser saat ini (periksa tabel ES6 kangax untuk kompatibilitas). Anda dapat menggunakan generator es2015 untuk ini. Saya telah memperbarui jawaban TheHippo. Tentu saja jika Anda benar-benar menginginkan dukungan IE Anda dapat menggunakan babel JavaScript transpiler.
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
function* traverse(o, path=[]) {
for (var i in o) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* traverse(o[i], itemPath);
}
}
}
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Jika Anda hanya ingin memiliki properti enumerable (pada dasarnya properti rantai non-prototipe), Anda dapat mengubahnya untuk beralih menggunakan Object.keys
dan for...of
loop sebagai gantinya:
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
function* traverse(o,path=[]) {
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* traverse(o[i],itemPath);
}
}
}
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}