2013-07-10 4 views
10

У меня есть объект папок/файлов выглядит следующим образом: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 с рекурсией?

ответ

11

Это не самый красивый код, но я проверил это, и это, кажется, работает так, как вы просите. Это настройка как lodash/underscore mixin, но может использоваться. Использование будет выглядеть так:

_.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); 

} 
+0

Возврат false для того, чтобы lodash вырвался из цикла, если значение найдено, не уверен, поддерживает ли подчеркивание, просматривая код, возможно, возвращение {} приведет к его разрыву, но я не уверен. – dariusriggins

+0

Отлично работает, спасибо тонну! – wesbos

+0

Отличное решение, работает как шарм. Интересно, почему это не встроено в US/LD по умолчанию! – dbau

9

Вместо 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 to call itself recursively.


Here's a working demo.

+0

Возможно ли это без «subl-child.html» жестко закодировано? – wesbos

+0

@ Мы уверены, просто добавьте его как еще один параметр рядом с 'searchText', я думаю? См. Здесь: http://jsfiddle.net/Fy9Ej/1/ – McGarnagle

+0

Извините, я хочу найти объект, где path === string. Независимо от уровня верхнего уровня или уровня 100. Здесь нет двух строк. – wesbos