web-dev-qa-db-ja.com

Underscore.jsfindWhereネストされたオブジェクト

次のようなフォルダ/ファイルのオブジェクトがあります。

{
  about.html : {
    path : './about.html'
  },
  about2.html : {
    path : './about2.html'
  },
  about3.html : {
    path : './about3.html'
  },
  folderName : {
    path : './folderName',
    children : {
      sub-child.html : {
        path : 'folderName/sub-child.html'
      }
    }
  }
}

また、子を持つフォルダの6〜7レベルの深さまで移動できます。

pathが指定した文字列と等しいオブジェクトを見つけたいと思います。深さに関係なく。

トップレベルのみを実行するアンダースコアを使用しています:

_.findWhere(files,{path:'./about2.html'}

ネストされた詳細な検索を実行するにはどうすればよいですか。アンダースコアにはこれに対する何かがありますか、それとも再帰を使用してミックスインを構築する必要がありますか?

10
wesbos

これは最も美しいコードではありませんが、私はそれをテストしました、そしてそれはあなたが求めているように動作するようです。 lodash/underscoreミックスインとして設定されていますが、使用できます。使用法は次のようになります。

_.findDeep(testItem, { 'path': 'folderName/sub-child.html' })

実装:

findDeep: function(items, attrs) {

  function match(value) {
    for (var key in attrs) {
      if(!_.isUndefined(value)) {
        if (attrs[key] !== value[key]) {
          return false;
        }
      }
    }

    return true;
  }

  function traverse(value) {
    var result;

    _.forEach(value, function (val) {
      if (match(val)) {
        result = val;
        return false;
      }

      if (_.isObject(val) || _.isArray(val)) {
        result = traverse(val);
      }

      if (result) {
        return false;
      }
    });

    return result;
  }

  return traverse(items);

}
11
dariusriggins

findWhereの代わりに、filterを使用します。これは、キーと値のマップではなく、述語として関数を取ります。再帰関数を使用して、現在のノードと可能な子を確認します。このようなもの:

_var searchText = './about2.html';

var recursiveFilter = function(x) {
    return x.path == searchText || 
        ( typeof x.children != 'undefined' && recursiveFilter(x.children['sub-child.html']) );
};

_.filter(files, recursiveFilter);
_

編集

これが機能すると仮定すると、おそらく関数getRecursiveFilter(searchText)を作成することをお勧めします。これがどのように見えるかです:

_function getRecursiveFilter(searchText) { 
    var recursiveFilter = function(x) {
        return x.path == searchText || 
            (typeof x.children != 'undefined' 
                && arguments.callee(x.children['sub-child.html']) );
    };
    return  recursiveFilter;
}
_

ここで、recursiveFilter_arguments.callee_を使用して自分自身を再帰的に呼び出す であることに注意してください。


これが動作するデモです。

9
McGarnagle

これはすでに受け入れられた答えを持っていますが、この他の答えは非常にきれいで、私の同様の状況に最適でした: https://stackoverflow.com/a/21600748/1913975_.filter + _.where

5
abenrob

受け入れられた回答は機能しますが、一般的すぎます。オブジェクトのすべてのプロパティを検索して子を見つけます。オブジェクトの奥深くにあると見なされる「recursProperty」と呼ばれる追加のパラメーターを導入することを提案しています。このソリューションは、lodash /アンダースコアミックスインとして使用するように設定されており、loadash /アンダースコア機能を拡張します。

_.findDeep = function(collection, predicate, recursProperty){
    let items = [];
    _.each(collection, each => items.Push(each));
    return _.find(items, function(value, key, coll){
        if (predicate(value, key, coll)){
            return true;
        } else {
            _.each(value[recursProperty], each => items.Push(each));
        }
    });
};

他のアンダースコア関数として使用できます。例えば、

_.findDeep(self.baseEntities, baseEntity => baseEntity.id === 71, 'entity');

'recursProperty'引数に適切な値を指定しないか、null/undefinedを指定すると、検索は最初のレベルでのみ行われます(深くはなりません)。

1
adhirohah