回答:
jQueryがこのような原始的なタスクに対して一種のやり過ぎだと思う場合は、次のようにすることができます。
//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
ターゲット関数の値ですが、関数o
の最初のパラメーターである必要があります。this
(traverse
関数になる)に設定するのは少し奇妙ですが、とにかくリファレンスをprocess
使用するようなものではありthis
ません。それも同様にnullでした。
/*jshint validthis: true */
上記を追加する必要があるかもしれませんfunc.apply(this,[i,o[i]]);
W040: Possible strict violation.
this
traverse
深さを追跡する関数に3つのパラメーターを追加できます。Wenn呼び出しは、現在のレベルに再帰的に1を加えます。
JSONオブジェクトは単なるJavaScriptオブジェクトです。それが実際にJSONは、JavaScript Object Notationの略です。したがって、JSONオブジェクトをトラバースしますが、一般的にはJavaScriptオブジェクトを「トラバース」することを選択します。
ES2017では、次のようにします。
Object.entries(jsonObj).forEach(([key, value]) => {
// do something with key and val
});
オブジェクトに再帰的に下降する関数をいつでも作成できます。
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
}
}
これは良い出発点になるはずです。このようなものには、最新のjavascriptメソッドを使用することを強くお勧めします。これらのメソッドを使用すると、このようなコードの記述がはるかに簡単になります。
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'
JavaScriptを使用してJSONデータをトラバースするための新しいライブラリーがあり、さまざまなユースケースをサポートしています。
https://npmjs.org/package/traverse
https://github.com/substack/js-traverse
すべての種類のJavaScriptオブジェクトで動作します。サイクルも検出します。
各ノードのパスも提供します。
あなたが何をしたいかに依存します。JavaScriptオブジェクトツリーをたどり、キーと値を表示する例を以下に示します。
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
実際のJSON 文字列をトラバースする場合は、リバイバー関数を使用できます。
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)
})
オブジェクトをトラバースする場合:
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)
})
編集:この回答の以下の例はすべて、@ supersanの要求に従ってイテレーターから生成された新しいパス変数を含むように編集されています。パス変数は文字列の配列であり、配列内の各文字列は、元のソースオブジェクトからの結果の反復値を取得するためにアクセスされた各キーを表します。パス変数は、lodashのget function / methodに供給することができます。または、次のように配列のみを処理する独自のバージョンのlodashのgetを作成することもできます。
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"]));
編集:この編集された回答は、無限ループ走査を解決します。
この編集された答えは、私の元の答えの追加の利点の1つを提供します。これにより、提供されたジェネレーター関数を使用して、よりクリーンでシンプルな反復可能なインターフェースを使用できます(for of
ループが、反復可能で反復可能の要素であるfor(var a of b)
場合のようにループを使用すると考えてください))。あなたは、オブジェクトのプロパティに深く反復処理したいどこでも反復ロジックを繰り返す必要はありません、それはまたすることが可能となるので、それはまたそれを作ることにより、コードの再利用に役立ちますシンプルなAPIであることとともに、発電機能を使用することにより外ループを早く停止したい場合。b
a
break
JavaScriptオブジェクトは自己参照である可能性があるため、対処されておらず、私の元の答えにはないことの1つは、任意の(つまり、「ランダムな」セットの)オブジェクトを注意深くトラバースする必要があることです。これにより、無限ループ走査を行う機会が生まれます。ただし、変更されていないJSONデータは自己参照できないため、JSオブジェクトのこの特定のサブセットを使用している場合は、無限ループトラバーサルを心配する必要はなく、元の回答や他の回答を参照できます。次に、終了しない走査の例を示します(それ以外の場合はブラウザーのタブがクラッシュするため、実行可能なコードではないことに注意してください)。
また、編集した例のジェネレーターオブジェクトでは、オブジェクトの非プロトタイプキーのみを反復するObject.keys
代わりに使用することを選択しfor in
ました。プロトタイプキーを含めたい場合は、これを自分で交換できます。両方の実装については、以下の私の元の答えのセクションを参照してくださいObject.keys
し、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);
}
これから身を守るために、クロージャー内にセットを追加できます。これにより、関数が最初に呼び出されたときに、それが見たオブジェクトのメモリの構築を開始し、すでに見たオブジェクトに出くわしたら繰り返しを継続しません。以下のコードスニペットはこれを実行し、無限ループのケースを処理します。
//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のドロップを気にせず、主に最新のブラウザーをサポートしている場合の新しい方法については、互換性についてkangaxのes6テーブルを確認してください。これにはes2015 ジェネレーターを使用できます。私はそれに応じて@TheHippoの回答を更新しました。もちろん、IEサポートが本当に必要な場合は、babel JavaScriptトランスパイラーを使用できます。
//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);
}
列挙可能なプロパティ(基本的に非プロトタイプチェーンプロパティ)のみが必要な場合はObject.keys
、for...of
代わりにとループを使用して反復するように変更できます。
//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);
}
@TheHippoの完全なソリューションを、プロセスおよびトリガー関数を使用せずに、匿名関数で使用したかったのです。以下は私のために働き、私のような初心者プログラマーのために共有しました。
(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);
ほとんどのJavaScriptエンジンは末尾再帰を最適化しません(JSONが深くネストされていない場合、これは問題にならない可能性があります)が、通常は注意を怠って代わりに反復を行います。たとえば、
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)
私のスクリプト:
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:
[
{
"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": []
}
]
}
]
}
]
関数呼び出し:
callback_func(inp_json);
私の必要に応じて出力:
["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>
すべてのキー/値を取得し、これで階層を保持できます
// 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')
深くネストされたJSオブジェクトをトラバースおよび編集するためのライブラリを作成しました。ここでAPIを確認してください:https : //github.com/dominik791
デモアプリを使用してインタラクティブにライブラリを再生することもできます:https : //dominik791.github.io/obj-traverse-demo/
使用例:各メソッドの最初のパラメーターであるルートオブジェクトが常に必要です。
var rootObj = {
name: 'rootObject',
children: [
{
'name': 'child1',
children: [ ... ]
},
{
'name': 'child2',
children: [ ... ]
}
]
};
2番目のパラメーターは常に、ネストされたオブジェクトを保持するプロパティの名前です。上記の場合はそうなります'children'
。
3番目のパラメーターは、検索/変更/削除するオブジェクトを検索するために使用するオブジェクトです。たとえば、idが1のオブジェクトを探している場合は{ id: 1}
、3番目のパラメータとして渡します。
そして、次のことができます:
findFirst(rootObj, 'children', { id: 1 })
で最初のオブジェクトを見つける id === 1
findAll(rootObj, 'children', { id: 1 })
すべてのオブジェクトを検索するには id === 1
findAndDeleteFirst(rootObj, 'children', { id: 1 })
最初に一致したオブジェクトを削除するにはfindAndDeleteAll(rootObj, 'children', { id: 1 })
一致するすべてのオブジェクトを削除するにはreplacementObj
最後の2つのメソッドの最後のパラメーターとして使用されます。
findAndModifyFirst(rootObj, 'children', { id: 1 }, { id: 2, name: 'newObj'})
変化に最初にオブジェクトを見つけid === 1
に{ id: 2, name: 'newObj'}
findAndModifyAll(rootObj, 'children', { id: 1 }, { id: 2, name: 'newObj'})
ですべてのオブジェクトを変更するid === 1
に{ id: 2, name: 'newObj'}