45

我有一个巨大的收藏,我正在寻找收藏中某处的钥匙。获取包含该键/索引的所有对象的引用列表或完整路径的可靠方法是什么?如果有帮助,我会使用 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': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd');
// [o.cc,o.cc.dd]

fwiw lodash 有一个 _.find 函数,可以找到两个嵌套深度的嵌套对象,但之后似乎失败了。(例如http://codepen.io/anon/pen/bnqyh

4

10 回答 10

50

这应该这样做:

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;
}
于 2013-03-26T17:15:16.197 回答
24

纯 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}]
于 2013-03-26T17:15:15.160 回答
5

这将在一个对象数组(干草)中深度搜索一个值(针),然后返回一个包含结果的数组......

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;
}
于 2015-02-24T15:46:47.770 回答
3

使用 Deepdash,您可以pickDeep然后从中获取路径,或者索引(构建路径-> 值对象)

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":{}}

这是一个Codepen 演示

于 2019-03-11T09:12:07.123 回答
3

如果你可以用普通的 JS(或结合 lodash)编写一个递归函数,那将是最好的(按性能),但是如果你想从你身边跳过递归并想要一个简单的可读代码(这可能不是根据性能最好),那么您可以将lodash#cloneDeepWith用于必须递归遍历对象的任何目的。

let findValuesDeepByKey = (obj, key, res = []) => (
    _.cloneDeepWith(obj, (v,k) => {k==key && res.push(v)}) && res
)

因此,您作为 的第二个参数传递的回调_.cloneDeepWith将递归地遍历所有key/value对,您所要做的就是您想要对每个对执行的操作。上面的代码只是您的案例的一个例子。这是一个工作示例:

var object = {
    prop1: 'ABC1',
    prop2: 'ABC2',
    prop3: {
        prop4: 'ABC3',
        prop5Arr: [{
                prop5: 'XYZ'
            },
            {
                prop5: 'ABC4'
            },
            {
                prop6: {
                    prop6NestedArr: [{
                            prop1: 'XYZ Nested Arr'
                        },
                        {
                            propFurtherNested: {key100: '100 Value'}
                        }
                    ]
                }
            }
        ]
    }
}
let findValuesDeepByKey = (obj, key, res = []) => (
    _.cloneDeepWith(obj, (v,k) => {k==key && res.push(v)}) && res
)

console.log(findValuesDeepByKey(object, 'prop1'));
console.log(findValuesDeepByKey(object, 'prop5'));
console.log(findValuesDeepByKey(object, 'key100'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

于 2018-08-04T19:37:12.290 回答
2

像这样的东西会起作用,将其转换为对象并向下递归。

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

于 2013-03-26T16:58:22.560 回答
1

如果您没有看到来自@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;
}
于 2020-10-01T19:42:59.560 回答
0

我们将对象扫描用于数据处理任务。一旦你了解了如何使用它,它就非常棒了。

// const objectScan = require('object-scan');

const haystack = { a: { b: { c: 'd' }, e: { f: 'g' } } };
const r = objectScan(['a.*.*'], { joined: true, rtn: 'entry' })(haystack);
console.log(r);
// => [ [ 'a.e.f', 'g' ], [ 'a.b.c', 'd' ] ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.8.0"></script>

免责声明:我是对象扫描的作者

网站上还有很多例子。

于 2020-10-25T03:35:48.750 回答
0
Array.prototype.findpath = function(item,path) {
  return this.find(function(f){return item==eval('f.'+path)});
}
于 2016-11-17T15:45:48.077 回答
0

我是这样做的:

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 ]
于 2016-11-29T01:00:15.307 回答