我在Javascript中有一个如下的对象,
const obj = {
"group1": {
"sub_group1_1": {
"inner_sub_group1_1_1": {
"name": "abc"
},
"inner_sub_group1_1_2": {
"name": "def"
}
},
"sub_group1_2": {
"inner_sub_group1_2_1": {
"name": "ghi"
},
"inner_sub_group1_2_2": {
"name": "jkl"
}
}
},
"group2": {
"sub_group2_1": {
"inner_sub_group2_1_1": {
"name": "mno"
},
"inner_sub_group2_1_2": {
"name": "pqr"
}
},
"sub_group2_2": {
"inner_sub_group2_2_1": {
"name": "stu"
},
"inner_sub_group2_2_2": {
"name": "wxy"
}
}
}
}
我想写一个函数,它可以在上面的对象中搜索一个密钥,并向我返回该密钥的相应值,例如
输入
filterObject(obj, 'inner_sub_group2_2_2')
输出
{
"name": "wxy
}
输入
filterObject(obj, 'inner_sub_group1_1_1')
输出
{
"name": "abc
}
方法
我已经为它编写了一个递归函数,但它似乎不适用于第二种情况,
const filterObject = (obj, searchedKey) => {
let result = {};
for (const key in obj) {
const currentObj = obj[key];
if (key === searchedKey) {
result = currentObj;
break;
} else if (typeof currentObj === 'object') {
result = filterObject(currentObj, searchedKey);
}
}
return result;
};
任何形式的帮助都将不胜感激。谢谢
您需要能够检查递归调用是否找到了什么,而您没有这样做——现在,您只是重新分配result
,然后忽略它,继续循环的下一次迭代。
一种适用于这种特殊情况的方法是检查结果对象是否有任何键。
const obj={group1:{sub_group1_1:{inner_sub_group1_1_1:{name:"abc"},inner_sub_group1_1_2:{name:"def"}},sub_group1_2:{inner_sub_group1_2_1:{name:"ghi"},inner_sub_group1_2_2:{name:"jkl"}}},group2:{sub_group2_1:{inner_sub_group2_1_1:{name:"mno"},inner_sub_group2_1_2:{name:"pqr"}},sub_group2_2:{inner_sub_group2_2_1:{name:"stu"},inner_sub_group2_2_2:{name:"wxy"}}}};
const filterObject = (obj, searchedKey) => {
if (searchedKey in obj) {
return obj[searchedKey];
}
for (const key in obj) {
const currentObj = obj[key];
if (typeof currentObj === 'object') {
const result = filterObject(currentObj, searchedKey);
if (Object.keys(result).length) return result;
}
}
return {};
};
console.log(filterObject(obj, 'inner_sub_group2_2_2'));
console.log(filterObject(obj, 'inner_sub_group1_1_1'));
但是,如果找到的值不是对象,或者找到的对象为空,则这将不起作用。(将返回与原始结构未连接的不同对象引用。)
对于更一般的情况,递归调用应该返回两件事:是否找到嵌套值,以及找到的值(如果有的话)。一种方法是,如果找到了某个对象,则返回该对象,而不返回其他对象。
const obj={group1:{sub_group1_1:{inner_sub_group1_1_1:{name:"abc"},inner_sub_group1_1_2:{name:"def"}},sub_group1_2:{inner_sub_group1_2_1:{name:"ghi"},inner_sub_group1_2_2:{name:"jkl"}}},group2:{sub_group2_1:{inner_sub_group2_1_1:{name:"mno"},inner_sub_group2_1_2:{name:"pqr"}},sub_group2_2:{inner_sub_group2_2_1:{name:"stu"},inner_sub_group2_2_2:{name:"wxy"}}}};
const filterObject = (obj, searchedKey) => {
if (searchedKey in obj) {
return { result: obj[searchedKey] };
}
for (const key in obj) {
const currentObj = obj[key];
if (typeof currentObj === 'object') {
const result = filterObject(currentObj, searchedKey);
if (result) return result;
}
}
};
console.log(filterObject(obj, 'inner_sub_group2_2_2').result);
console.log(filterObject(obj, 'inner_sub_group1_1_1').result);
代码中的问题是,如果递归调用成功,循环仍在继续,同时进行另一个可能不成功的递归调用,因此丢失了良好的result
。
在那个递归调用之后,您需要一个if
,它检测成功并在成功的情况下爆发。
现在,为了区分失败和成功,如果在没有成功的情况下返回null
,事情会变得更容易。
需要调整两条线路:
const filterObject = (obj, searchedKey) => {
let result = null; // To indicate nothing found yet
for (const key in obj) {
const currentObj = obj[key];
if (key === searchedKey) {
result = currentObj;
break;
} else if (typeof currentObj === 'object') {
result = filterObject(currentObj, searchedKey);
if (result) break; // <--- break out when success!
}
}
return result;
};
您的is对象测试可能需要一些改进,因为typeof null === 'object'
是一个真实的表达式。
你可以用这个代替:
if (Object(currentObj) === currentObj)
以下是@trincot和@secuneperformance答案的变体。没有什么新的东西,只是略有不同的风格:
const obj={group1:{sub_group1_1:{inner_sub_group1_1_1:{name:"abc"},inner_sub_group1_1_2:{name:"def"}},sub_group1_2:{inner_sub_group1_2_1:{name:"ghi"},inner_sub_group1_2_2:{name:"jkl",spoiler:null}}},group2:{sub_group2_1:{inner_sub_group2_1_1:{name:"mno"},inner_sub_group2_1_2:{name:"pqr",xtr:false}},sub_group2_2:{inner_sub_group2_2_1:{name:"stu"},inner_sub_group2_2_2:{name:"wxy"}}}};
function deepGet(obj,k,r){ // call with two arguments only
if(obj&&typeof obj=="object") {
if(k in obj) return obj[k];
for(l in obj) if((r=deepGet(obj[l],k))!=null) return r;
}
return null;
}
console.log(['inner_sub_group2_2_2','inner_sub_group1_1_1','xtr'].map(k=>deepGet(obj,k)));