Javascript 在深层对象中按名称查找属性
我有一个庞大的收集,我正在寻找一个关键的财产内的某个地方收集。获取包含该键/索引的所有对象的引用列表或完整路径的可靠方法是什么?如果有帮助的话,我使用jQuery和lodash,您可以忘记无限指针递归,这是一个纯JSON响应Javascript 在深层对象中按名称查找属性,javascript,jquery,lodash,Javascript,Jquery,Lodash,我有一个庞大的收集,我正在寻找一个关键的财产内的某个地方收集。获取包含该键/索引的所有对象的引用列表或完整路径的可靠方法是什么?如果有帮助的话,我使用jQuery和lodash,您可以忘记无限指针递归,这是一个纯JSON响应 fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "d"); // [o.c] fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "e"); // [o.c.d] fn({ 'aa': 1, 'bb'
fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "d");
// [o.c]
fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "e");
// [o.c.d]
fn({ 'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd');
// [o.cc,o.cc.dd]
fwiw-lodash有一个u.find函数,该函数将查找两个嵌套深度的嵌套对象,但之后它似乎失败了。(例如)类似的东西可以工作,将其转换为对象并向下递归
function find(jsonStr, searchkey) {
var jsObj = JSON.parse(jsonStr);
var set = [];
function fn(obj, key, path) {
for (var prop in obj) {
if (prop === key) {
set.push(path + "." + prop);
}
if (obj[prop]) {
fn(obj[prop], key, path + "." + prop);
}
}
return set;
}
fn(jsObj, searchkey, "o");
}
Fiddle:纯JavaScript解决方案如下所示:
function findNested(obj, key, memo) {
var i,
proto = Object.prototype,
ts = proto.toString,
hasOwn = proto.hasOwnProperty.bind(obj);
if ('[object Array]' !== ts.call(memo)) memo = [];
for (i in obj) {
if (hasOwn(i)) {
if (i === key) {
memo.push(obj[i]);
} else if ('[object Array]' === ts.call(obj[i]) || '[object Object]' === ts.call(obj[i])) {
findNested(obj[i], key, memo);
}
}
}
return memo;
}
以下是如何使用此功能:
findNested({'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd');
结果是:
[{x: 9}, {y: 9}]
这应该做到:
function fn(obj, key) {
if (_.has(obj, key)) // or just (key in obj)
return [obj];
// elegant:
return _.flatten(_.map(obj, function(v) {
return typeof v == "object" ? fn(v, key) : [];
}), true);
// or efficient:
var res = [];
_.forEach(obj, function(v) {
if (typeof v == "object" && (v = fn(v, key)).length)
res.push.apply(res, v);
});
return res;
}
这将深入搜索对象数组(hay)中的值(指针),然后返回一个包含结果的数组
search = function(hay, needle, accumulator) {
var accumulator = accumulator || [];
if (typeof hay == 'object') {
for (var i in hay) {
search(hay[i], needle, accumulator) == true ? accumulator.push(hay) : 1;
}
}
return new RegExp(needle).test(hay) || accumulator;
}
我是这样做的:
function _find( obj, field, results )
{
var tokens = field.split( '.' );
// if this is an array, recursively call for each row in the array
if( obj instanceof Array )
{
obj.forEach( function( row )
{
_find( row, field, results );
} );
}
else
{
// if obj contains the field
if( obj[ tokens[ 0 ] ] !== undefined )
{
// if we're at the end of the dot path
if( tokens.length === 1 )
{
results.push( obj[ tokens[ 0 ] ] );
}
else
{
// keep going down the dot path
_find( obj[ tokens[ 0 ] ], field.substr( field.indexOf( '.' ) + 1 ), results );
}
}
}
}
使用以下工具进行测试:
var obj = {
document: {
payload: {
items:[
{field1: 123},
{field1: 456}
]
}
}
};
var results = [];
_find(obj.document,'payload.items.field1', results);
console.log(results);
输出
[ 123, 456 ]
如果您可以用纯JS(或与lodash的组合)编写递归函数,这将是最好的(根据性能),但是如果您希望跳过递归,并希望使用简单可读的代码(根据性能可能不是最好的),那么您可以将其用于任何需要递归遍历对象的目的
let findValuesDeepByKey = (obj, key, res = []) => (
_.cloneDeepWith(obj, (v,k) => {k==key && res.push(v)}) && res
)
因此,您作为\uu1.cloneDeepWith
的第二个参数传递的回调将递归地遍历所有键/值
对,您所要做的就是对每个键/值进行操作。上面的代码只是您案例的一个示例。以下是一个工作示例:
var对象={
提案1:‘ABC1’,
方案二:“ABC2”,
提案3:{
方案4:‘ABC3’,
prop5Arr:[{
prop5:'XYZ'
},
{
方案五:“ABC4”
},
{
建议6:{
prop6NestedArr:[{
prop1:'XYZ嵌套Arr'
},
{
属性:{key100:'100 Value'}
}
]
}
}
]
}
}
让FindValuesDepByKey=(对象、键、结果=[])=>(
_.cloneDeepWith(obj,(v,k)=>{k==键和res.push(v)})和res
)
log(findValuesDepByKey(对象“prop1”);
log(findValuesDepbyKey(对象“prop5”);
log(findValuesDepbyKey(对象“key100”)代码>
使用Deepdash,您可以从中获取,或者(构建路径->值对象)
这里有一个如果你看不到@eugene的更新答案,这个调整允许传递一个要搜索的键列表
// Method that will find any "message" in the Apex errors that come back after insert attempts
// Could be a validation rule, or duplicate record, or pagemessage.. who knows!
// Use in your next error toast from a wire or imperative catch path!
// message: JSON.stringify(this.findNested(error, ['message', 'stackTrace'])),
// Testing multiple keys: this.findNested({thing: 0, list: [{message: 'm'}, {stackTrace: 'st'}], message: 'm2'}, ['message', 'stackTrace'])
findNested(obj, keys, memo) {
let i,
proto = Object.prototype,
ts = proto.toString,
hasOwn = proto.hasOwnProperty.bind(obj);
if ('[object Array]' !== ts.call(memo)) memo = [];
for (i in obj) {
if (hasOwn(i)) {
if (keys.includes(i)) {
memo.push(obj[i]);
} else if ('[object Array]' === ts.call(obj[i]) || '[object Object]' === ts.call(obj[i])) {
this.findNested(obj[i], keys, memo);
}
}
}
return memo.length == 0 ? null : memo;
}
我们用于数据处理任务。一旦你掌握了如何使用它,它就非常棒了
//const objectScan=require('object-scan');
常量haystack={a:{b:{c:'d'},e:{f:'g'};
const r=objectScan(['a.*.]],{joined:true,rtn:'entry'})(haystack);
控制台日志(r);
//=>
。作为控制台包装{最大高度:100%!重要;顶部:0}
这几乎对我有效。我不得不将第3行更改为return[obj[key]改为code>,这样我就可以得到一个键值数组,而不是包装object@ChrisMontgomery:是的,但OP想要“包含该键的所有对象”(可能他正在对结果执行pull()
)@Bergi是有道理的。您的解决方案适用于我的所有场景,而Al-Jey的答案在对象中的数组中的对象存在问题。res.push(v)
和res.push.apply(res,v)之间有什么区别
?@MichaelTrouw:实际上,如果满足if
条件,则v
始终是一个数组,因为fn
始终返回一个数组。我们在这里使用push.apply
将多个项一次附加到res
数组中,就像res=res.concat(v)
那样,但不创建新数组。此解决方案不适用于嵌套数组,例如findNested({'a':[{'b':{'c':7}},'b')
而@Bergidoes@ChrisMontgomery,这是因为最初的问题只包括对象,没有任何数组,我已经更新了示例。@AlJey,对于你的lodash示例,我使用了var o={a:{really:'long'},obj:{that:{keeps:{keeps:'going'}}}
然后findNested(o,'that')
,这给了我范围错误:超过了最大调用堆栈大小
。第一个工程辉煌虽然!Uncaught RangeError:最大调用堆栈大小超出了Di在上使用它的大小window@ryanyuyu-可读,不接近缩小。这是通俗易懂的英语,但确实需要相当多的智商才能理解。这是一个美丽而复杂的解决方案,它将大脑的压力压到了极限:)@vsync代码已经被编辑过了。请参阅“您应避免使用“仅限代码”答案”。请提供有关代码的一些上下文。我在尝试搜索窗口对象上的属性时遇到了超出最大调用堆栈的错误。是否启用了循环检查选项?默认情况下是关闭的。共享您的代码,我将帮助修复它代码>我得到了错误:TypeError:instanceof'的右边不是一个对象
我没有用于测试的Netscape浏览器,但这在chrome中是有效的:(function(){var ld=window.;window.=null;var cherry=ld.pickDeep(window,'Netscape',{checkCircular:true});console.log({cherry});})代码>当deepdash试图迭代它所依赖的全局lodash对象时,会发生一些奇怪的事情,所以将其移动到某个局部范围。我得到了完全相同的错误。每次我的应用程序的本地存储也会被清除,这很奇怪。我运行了以下命令:(function(){var-ld=u;deepdash(ld);u=null;var-cherry=ld.pickDee
var obj = { 'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}
var cherry = _.pickDeep(obj,"d");
console.log(JSON.stringify(cherry));
// {"cc":{"d":{}},"dd":{"d":{}}}
var paths = _.paths(cherry);
console.log(paths);
// ["cc.d", "dd.d"]
paths = _.paths(cherry,{pathFormat:'array'});
console.log(JSON.stringify(paths));
// [["cc","d"],["dd","d"]]
var index = _.indexate(cherry);
console.log(JSON.stringify(index));
// {"cc.d":{},"dd.d":{}}
// Method that will find any "message" in the Apex errors that come back after insert attempts
// Could be a validation rule, or duplicate record, or pagemessage.. who knows!
// Use in your next error toast from a wire or imperative catch path!
// message: JSON.stringify(this.findNested(error, ['message', 'stackTrace'])),
// Testing multiple keys: this.findNested({thing: 0, list: [{message: 'm'}, {stackTrace: 'st'}], message: 'm2'}, ['message', 'stackTrace'])
findNested(obj, keys, memo) {
let i,
proto = Object.prototype,
ts = proto.toString,
hasOwn = proto.hasOwnProperty.bind(obj);
if ('[object Array]' !== ts.call(memo)) memo = [];
for (i in obj) {
if (hasOwn(i)) {
if (keys.includes(i)) {
memo.push(obj[i]);
} else if ('[object Array]' === ts.call(obj[i]) || '[object Object]' === ts.call(obj[i])) {
this.findNested(obj[i], keys, memo);
}
}
}
return memo.length == 0 ? null : memo;
}