在深层对象中按名称查找属性
我有一个巨大的集合,我正在寻找一个属性收集内的关键某处。 获取包含该键/索引的所有对象的引用列表或完整path的可靠方法是什么? 我使用jQuery和lodash,如果它有帮助,你可以忘记无限指针recursion,这是一个纯粹的JSON响应。
fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "d"); // [oc] fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "e"); // [ocd] fn({ 'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd'); // [o.cc,o.cc.dd]
fwiw lodash有一个_.find函数,它可以find两个嵌套深的嵌套对象,但似乎在这之后失败了。 (例如http://codepen.io/anon/pen/bnqyh )
这应该做到这一点:
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; }
一个纯粹的JavaScript解决scheme将如下所示:
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; }
这里是你如何使用这个function:
findNested({'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd');
其结果是:
[{x: 9}, {y: 9}]
这将深入search对象(干草)的数组(针),然后返回一个数组结果…
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; }
像这样的东西会工作,将其转换为一个对象,并recursion下来。
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"); }
小提琴: jsfiddle
Array.prototype.findpath = function(item,path) { return this.find(function(f){return item==eval('f.'+path)}); }
以下是我如何做到的:
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 ); } } } }
testing它:
var obj = { document: { payload: { items:[ {field1: 123}, {field1: 456} ] } } }; var results = []; _find(obj.document,'payload.items.field1', results); console.log(results);
输出
[ 123, 456 ]