web-dev-qa-db-ja.com

ディープオブジェクトで名前でプロパティを検索する

膨大なコレクションがあり、コレクション内のどこかにキーでプロパティを探しています。そのキー/インデックスを含むすべてのオブジェクトへの参照またはフルパスのリストを取得するための信頼できる方法は何ですか? 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関数があり、2つのネストの深さであるネストされたオブジェクトを検索しますが、その後失敗するようです。 (例 http://codepen.io/anon/pen/bnqyh

38
Shanimal

これはそれを行う必要があります:

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;
}
46
Bergi

純粋な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}]
23
Eugene Kuzmenko

これにより、オブジェクトの配列(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;
}
5
Eldad

プレーンなJS(またはlodashの組み合わせ)で再帰関数を(パフォーマンスで)最適に作成できるが、あなたの側から再帰をスキップし、単純な可読コード(これはパフォーマンスに応じて最適になります)、オブジェクトを再帰的にトラバースする必要があるあらゆる目的で lodash#cloneDeepWith を使用できます。

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

そのため、_.cloneDeepWithの2番目の引数として渡すコールバックは、すべての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>
2

Deepdashを使用すると、 pickDeep から paths を取得できます。または indexate (build-> valueオブジェクト)

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デモ

2
Yuri Gor

このようなものが機能し、それをオブジェクトに変換して下に再帰します。

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

2
Ben McCormick

以下がその方法です。

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 ]
0
lewma
Array.prototype.findpath = function(item,path) {
  return this.find(function(f){return item==eval('f.'+path)});
}
0
lacmuch