Underscore.js findWhere вложенные объекты

У меня есть объект папок/файлов, который выглядит так:

{
  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 уровней глубоких папок с детьми.

Я хочу найти объект, где путь равен строке, которую я предоставляю. Независимо от глубины.

Я использую символ подчеркивания, который имеет только верхний уровень:

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

Как сделать глубокий, вложенный поиск. У подчеркивания есть что-то для этого или мне нужно создать mixin с рекурсией?

Ответ 1

Это не самый красивый код, но я тестировал его и, похоже, работал так, как вы просите. Он настраивается как смесь 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);

}

Ответ 2

Вместо findWhere используйте filter, который принимает функцию как предикат, а не карту значения ключа. Используйте рекурсивную функцию для проверки текущего node и возможных детей. Что-то вроде этого:

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 для рекурсивного вызова.


Здесь рабочая демонстрация.

Ответ 3

У этого уже есть принятый ответ, но этот ответ был очень чистым и идеальным для моей подобной ситуации: fooobar.com/info/262890/... _.filter + _.where