Underscore.js findWhere嵌套对象

11

我有一个文件夹和文件的对象,长这样:

{
  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层包含子文件夹的目录。

我想找到路径等于我提供的字符串的对象。无论有多深。

我正在使用underscore,它只能处理顶级:

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

我该如何进行深度嵌套的搜索?Underscore 库中是否有此功能,还是需要使用递归构建 mixin?

4个回答

12

这不是最美观的代码,但我测试过它,似乎可以按照您的要求工作。它是设置为 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);

}

如果找到一个值,return false就会让lodash跳出循环,不确定underscore是否支持该功能。浏览了代码后,或许返回{}可以让它跳出循环,但我不确定。 - dariusriggins
很棒的解决方案,运行得非常顺畅。我不知道为什么这不是默认内置于US/LD中的! - dbau
@terox,你能详细解释一下吗?我不相信这个,因为对象中也可能存在循环,所以只需按照路线走即可... - user57508

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来进行递归调用

这是一个可工作的演示。


有没有可能不硬编码'subl-child.html'呢? - wesbos
@Wes 确定,只需将其作为 searchText 旁边的另一个参数添加即可,我猜?请参见此处:http://jsfiddle.net/Fy9Ej/1/ - McGarnagle
抱歉,我想找到路径 === 字符串的对象。无论它是顶层还是深入100级。这里没有两个字符串。 - wesbos

6

2
这不是递归的。 - David

1
尽管被接受的答案可行,但它过于通用 - 它搜索对象的所有属性以查找子元素。我建议引入一个额外的参数,称为“recursProperty”,它将被认为深入对象。这个解决方案也可以作为lodash/underscore mixin使用,并扩展了loadash/underscore的功能。
_.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将仅在第一级进行搜索(不会深入搜索)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接