Bir JSON nesne ağacında gezinmek istiyorum, ancak bunun için herhangi bir kütüphane bulamıyorum. Zor görünmüyor ama tekerleği yeniden icat etmek gibi geliyor.
XML'de DOM ile bir XML ağacında nasıl gezinileceğini gösteren çok sayıda öğretici var :(
Bir JSON nesne ağacında gezinmek istiyorum, ancak bunun için herhangi bir kütüphane bulamıyorum. Zor görünmüyor ama tekerleği yeniden icat etmek gibi geliyor.
XML'de DOM ile bir XML ağacında nasıl gezinileceğini gösteren çok sayıda öğretici var :(
Yanıtlar:
Eğer jQuery'nin bu tür ilkel bir görev için aşırı derecede dolgun olduğunu düşünüyorsanız, böyle bir şey yapabilirsiniz:
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
//called with every property and its value
function process(key,value) {
console.log(key + " : "+value);
}
function traverse(o,func) {
for (var i in o) {
func.apply(this,[i,o[i]]);
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
traverse(o[i],func);
}
}
}
//that's all... no magic, no bloated framework
traverse(o,process);
this
hedef işlevdeki değerdir, oysa işlevin o
ilk parametresi olmalıdır. this
(Bu traverse
işlev olurdu) ayarlamak biraz garip, ama yine process
de this
referans kullanır gibi değil . Bu da boş olabilirdi.
/*jshint validthis: true */
yukarıda eklemeniz gerekebilirfunc.apply(this,[i,o[i]]);
W040: Possible strict violation.
this
traverse
, derinliği izleyen işleve 3 parametre ekleyebilirsiniz . Wenn çağrısı tekrar tekrar mevcut seviyeye 1 ekleyin.
Bir JSON nesnesi basitçe bir Javascript nesnesidir. Aslında JSON şu anlama gelir: JavaScript Object Notation. Böylece bir JSON nesnesini geçersiniz, ancak genel olarak bir Javascript nesnesini "geçmeyi" tercih edersiniz.
ES2017'de şunları yaparsınız:
Object.entries(jsonObj).forEach(([key, value]) => {
// do something with key and val
});
Nesneye yinelemeli olarak inmek için her zaman bir işlev yazabilirsiniz:
function traverse(jsonObj) {
if( jsonObj !== null && typeof jsonObj == "object" ) {
Object.entries(jsonObj).forEach(([key, value]) => {
// key is either an array index or object key
traverse(value);
});
}
else {
// jsonObj is a number or string
}
}
Bu iyi bir başlangıç noktası olmalı. Bu tür kodları yazmayı çok daha kolay hale getirdiğinden, bu tür şeyler için modern javascript yöntemlerini kullanmanızı şiddetle tavsiye ederim.
function traverse(jsonObj) { if(jsonObj && typeof jsonObj == "object" ) { ...
function traverse(o) {
for (var i in o) {
if (!!o[i] && typeof(o[i])=="object") {
console.log(i, o[i]);
traverse(o[i]);
} else {
console.log(i, o[i]);
}
}
}
much better
?
!!o[i] && typeof o[i] == 'object'
JSON verilerini JavaScript ile taramak için birçok farklı kullanım durumunu destekleyen yeni bir kütüphane var.
https://npmjs.org/package/traverse
https://github.com/substack/js-traverse
Her türlü JavaScript nesnesiyle çalışır. Döngüleri bile algılar.
Her düğümün yolunu da sağlar.
Ne yapmak istediğine bağlı. Aşağıda, bir JavaScript nesne ağacında gezinme, anahtarları ve değerleri gittikçe yazdırma örneği:
function js_traverse(o) {
var type = typeof o
if (type == "object") {
for (var key in o) {
print("key: ", key)
js_traverse(o[key])
}
} else {
print(o)
}
}
js> foobar = {foo: "bar", baz: "quux", zot: [1, 2, 3, {some: "hash"}]}
[object Object]
js> js_traverse(foobar)
key: foo
bar
key: baz
quux
key: zot
key: 0
1
key: 1
2
key: 2
3
key: 3
key: some
hash
Gerçek bir JSON dizesini geziyorsanız, bir reviver işlevi kullanabilirsiniz.
function traverse (json, callback) {
JSON.parse(json, function (key, value) {
if (key !== '') {
callback.call(this, key, value)
}
return value
})
}
traverse('{"a":{"b":{"c":{"d":1}},"e":{"f":2}}}', function (key, value) {
console.log(arguments)
})
Bir nesneyi gezerken:
function traverse (obj, callback, trail) {
trail = trail || []
Object.keys(obj).forEach(function (key) {
var value = obj[key]
if (Object.getPrototypeOf(value) === Object.prototype) {
traverse(value, callback, trail.concat(key))
} else {
callback.call(obj, key, value, trail)
}
})
}
traverse({a: {b: {c: {d: 1}}, e: {f: 2}}}, function (key, value, trail) {
console.log(arguments)
})
DÜZENLEME : Bu yanıttaki aşağıdaki tüm örnekler @ supersan'ın isteğine göre yineleyiciden gelen yeni bir yol değişkeni içerecek şekilde düzenlenmiştir . Path değişkeni, dizideki her dizenin orijinal kaynak nesneden elde edilen yinelenen değere ulaşmak için erişilen her anahtarı temsil ettiği bir dizeler dizisidir. Yol değişkeni, lodash'ın get işlevine / yöntemine beslenebilir . Ya da sadece dizileri işleyen lodash's get'in kendi versiyonunuzu yazabilirsiniz:
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 : Bu düzenlenmiş cevap sonsuz döngü geçişleri çözer.
Bu Düzenlenen cevap hala sağlanan kullanmanızı sağlar orijinal cevap eklenen faydaları birini sağlayan jeneratör işlevi daha temiz ve basit kullanmak için iterable arabirimini (kullanarak düşünmek for of
gibi döngüler for(var a of b)
nerede b
bir iterable ve a
iterable bir unsurdur ). Bunu yaparak da kod yeniden yardımcı olur basit bir api olmanın yanı sıra jeneratör fonksiyonu kullanarak size derinden bir nesnenin özelliklerine yineleme istediğiniz her yerde yineleme mantığı tekrarlamak zorunda kalmamak için ve aynı zamanda mümkün kılar break
dışı daha önce yinelemeyi durdurmak istiyorsanız döngü.
Hitap etmediğim ve orijinal cevabımda olmayan fark ettiğim bir şey, JavaScript nesneleri kendinden referanslı olabileceğinden, rastgele (yani herhangi bir "rastgele" küme) nesneden geçmeye dikkat etmeniz gerektiğidir. Bu sonsuz döngüsel geçişlere sahip olma fırsatı yaratır. Ancak değiştirilmemiş JSON verileri kendi kendine referans olamaz, bu nedenle JS nesnelerinin bu özel alt kümesini kullanıyorsanız sonsuz döngü geçişleri hakkında endişelenmenize gerek yoktur ve orijinal cevabımı veya diğer cevaplarıma başvurabilirsiniz. İşte bitmeyen bir geçişe bir örnek (çalıştırılabilir bir kod parçası olmadığına dikkat edin, aksi takdirde tarayıcı sekmenizi kilitler).
Ayrıca benim düzenlenmiş örnekte jeneratör nesnesinde ben sadece nesne üzerinde prototip olmayan anahtarları yinelemek Object.keys
yerine kullanmayı tercih etti for in
. Prototip anahtarlarının dahil edilmesini istiyorsanız bunu kendiniz değiştirebilirsiniz. Object.keys
Ve ile yapılan her iki uygulama için aşağıdaki orijinal yanıt bölümüme bakın for in
.
//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);
}
Kendinizi bundan kurtarmak için, bir kapatma içine bir küme ekleyebilirsiniz, böylece işlev ilk çağrıldığında, gördüğü nesnelerin bir belleğini oluşturmaya başlar ve zaten görülen bir nesneye rastladığında yinelemeye devam etmez. Aşağıdaki kod snippet'i bunu yapar ve böylece sonsuz döngü durumlarını işler.
//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);
}
IE'yi bırakmayı ve esas olarak daha güncel tarayıcıları desteklemezseniz bunu yapmanın daha yeni bir yolu için ( kangax'ın es6 tablosunu uyumluluk için kontrol edin ). Bunun için es2015 jeneratörlerini kullanabilirsiniz . @ TheHippo'nun cevabını buna göre güncelledim. Tabii ki gerçekten IE desteği istiyorsanız babel JavaScript transpiler kullanabilirsiniz.
//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);
}
Yalnızca kendi numaralandırılabilir özelliklere (temel olarak prototip olmayan zincir özellikleri) sahip olmak istiyorsanız, bunu kullanarak yineleme Object.keys
ve for...of
döngü yerine değiştirebilirsiniz:
//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);
}
İşlem ve tetikleyici işlevleri kullanmadan @TheHippo'nun mükemmel çözümünü anonim bir işlevde kullanmak istedim. Aşağıdakiler benim gibi çalıştı, benim gibi acemi programcıları paylaştı.
(function traverse(o) {
for (var i in o) {
console.log('key : ' + i + ', value: ' + o[i]);
if (o[i] !== null && typeof(o[i])=="object") {
//going on step down in the object tree!!
traverse(o[i]);
}
}
})
(json);
Çoğu Javascript motoru kuyruk özyinelemesini optimize etmez (JSON derinlemesine iç içe değilse bu bir sorun olmayabilir), ancak genellikle dikkatli olun ve bunun yerine yineleme yapın, örn.
function traverse(o, fn) {
const stack = [o]
while (stack.length) {
const obj = stack.shift()
Object.keys(obj).forEach((key) => {
fn(key, obj[key], obj)
if (obj[key] instanceof Object) {
stack.unshift(obj[key])
return
}
})
}
}
const o = {
name: 'Max',
legal: false,
other: {
name: 'Maxwell',
nested: {
legal: true
}
}
}
const fx = (key, value, obj) => console.log(key, value)
traverse(o, fx)
Senaryom:
op_needed = [];
callback_func = function(val) {
var i, j, len;
results = [];
for (j = 0, len = val.length; j < len; j++) {
i = val[j];
if (i['children'].length !== 0) {
call_func(i['children']);
} else {
op_needed.push(i['rel_path']);
}
}
return op_needed;
};
JSON girişi:
[
{
"id": null,
"name": "output",
"asset_type_assoc": [],
"rel_path": "output",
"children": [
{
"id": null,
"name": "output",
"asset_type_assoc": [],
"rel_path": "output/f1",
"children": [
{
"id": null,
"name": "v#",
"asset_type_assoc": [],
"rel_path": "output/f1/ver",
"children": []
}
]
}
]
}
]
İşlev Çağrısı:
callback_func(inp_json);
Benim ihtiyacına göre çıktı:
["output/f1/ver"]
var test = {
depth00: {
depth10: 'string'
, depth11: 11
, depth12: {
depth20:'string'
, depth21:21
}
, depth13: [
{
depth22:'2201'
, depth23:'2301'
}
, {
depth22:'2202'
, depth23:'2302'
}
]
}
,depth01: {
depth10: 'string'
, depth11: 11
, depth12: {
depth20:'string'
, depth21:21
}
, depth13: [
{
depth22:'2201'
, depth23:'2301'
}
, {
depth22:'2202'
, depth23:'2302'
}
]
}
, depth02: 'string'
, dpeth03: 3
};
function traverse(result, obj, preKey) {
if(!obj) return [];
if (typeof obj == 'object') {
for(var key in obj) {
traverse(result, obj[key], (preKey || '') + (preKey ? '[' + key + ']' : key))
}
} else {
result.push({
key: (preKey || '')
, val: obj
});
}
return result;
}
document.getElementById('textarea').value = JSON.stringify(traverse([], test), null, 2);
<textarea style="width:100%;height:600px;" id="textarea"></textarea>
Bununla tüm anahtarları / değerleri alabilir ve hiyerarşiyi koruyabilirsiniz
// get keys of an object or array
function getkeys(z){
var out=[];
for(var i in z){out.push(i)};
return out;
}
// print all inside an object
function allInternalObjs(data, name) {
name = name || 'data';
return getkeys(data).reduce(function(olist, k){
var v = data[k];
if(typeof v === 'object') { olist.push.apply(olist, allInternalObjs(v, name + '.' + k)); }
else { olist.push(name + '.' + k + ' = ' + v); }
return olist;
}, []);
}
// run with this
allInternalObjs({'a':[{'b':'c'},{'d':{'e':5}}],'f':{'g':'h'}}, 'ob')
Bu bir değişikliktir ( https://stackoverflow.com/a/25063574/1484447 )
Derin iç içe geçmiş JS nesnelerini dolaşmak ve düzenlemek için kütüphane oluşturdum. API'yı buradan kontrol edin: https://github.com/dominik791
Demo uygulamasını kullanarak kütüphaneyle etkileşimli olarak da oynayabilirsiniz: https://dominik791.github.io/obj-traverse-demo/
Kullanım örnekleri: Her zaman her yöntemin ilk parametresi olan kök nesneniz olmalıdır:
var rootObj = {
name: 'rootObject',
children: [
{
'name': 'child1',
children: [ ... ]
},
{
'name': 'child2',
children: [ ... ]
}
]
};
İkinci parametre her zaman iç içe nesneleri tutan özelliğin adıdır. Yukarıdaki durumda olurdu 'children'
.
Üçüncü parametre bulmak / değiştirmek / silmek istediğiniz nesneyi / nesneleri bulmak için kullandığınız bir nesnedir. Örneğin, kimliği 1'e eşit olan bir nesne arıyorsanız { id: 1}
, üçüncü parametre olarak geçersiniz .
Ve yapabilirsin:
findFirst(rootObj, 'children', { id: 1 })
ile ilk nesneyi bulmak id === 1
findAll(rootObj, 'children', { id: 1 })
ile tüm nesneleri bulmak için id === 1
findAndDeleteFirst(rootObj, 'children', { id: 1 })
eşleşen ilk nesneyi silmek içinfindAndDeleteAll(rootObj, 'children', { id: 1 })
eşleşen tüm nesneleri silmek içinreplacementObj
son iki yöntemde son parametre olarak kullanılır:
findAndModifyFirst(rootObj, 'children', { id: 1 }, { id: 2, name: 'newObj'})
değişime ilk önce nesneyi bulunan id === 1
için{ id: 2, name: 'newObj'}
findAndModifyAll(rootObj, 'children', { id: 1 }, { id: 2, name: 'newObj'})
ile tüm nesneleri değiştirmek id === 1
için{ id: 2, name: 'newObj'}