Javascript 使用lodash递归收集属性的值

Javascript 使用lodash递归收集属性的值,javascript,recursion,lodash,Javascript,Recursion,Lodash,对于嵌套的复杂对象或数组,我希望收集给定属性名称的所有值。例如: var structure = { name: 'alpha', array: [ { name: 'beta' }, { name: 'gamma' } ], object: { name: 'delta', array: [ { name: 'epsilon' } ] } }; //

对于嵌套的复杂对象或数组,我希望收集给定属性名称的所有值。例如:

var structure = {
    name: 'alpha',
    array: [
        { name: 'beta' },
        { name: 'gamma' }
    ],
    object: {
        name: 'delta',
        array: [
            { name: 'epsilon' }
        ]
    }
};

// expected result:  [ 'alpha', 'beta', 'gamma', 'delta', 'epsilon' ]
很明显,如何使用简单的JS来实现这一点,但是:使用lodash有什么优雅、简洁的方法吗

[编辑]下面的当前变量。欢迎提供更好的解决方案

function getPropertyRecursive(obj, property) {
    var values = [];
    _.each(obj, function(value, key) {
        if (key === property) {
            values.push(value);
        } else if (_.isObject(value)) {
            values = values.concat(getPropertyRecursive(value, property));
        }
    });
    return values;
}

我知道没有Lodash/下划线函数可以满足您的要求

那你打算怎么办?具体来说,您希望从聚合结构中提取所有
name
属性的值。我们如何概括这一点?换句话说,如果您希望在Lodash/下划线中添加此类功能,您将如何重新定义问题?毕竟,大多数人不想获得
name
属性的值。您可以创建一个通用函数,在其中提供所需属性的名称,但是……更抽象地说,您真正想要做的是访问聚合结构中的所有节点,并对它们进行处理。如果我们将JavaScript中的聚合结构看作是通用树,那么我们可以采用深度优先遍历的递归方法:

function walk(o, f) {
    f(o);
    if(typeof o !== 'object') return;
    if(Array.isArray(o)) 
      return o.forEach(e => walk(e, f));
    for(let prop in o) walk(o[prop], f);
}
现在,我们可以通过遍历结构并向数组中添加内容来完成您想要的操作:

const arr = [];
walk(structure, x => if(x !== undefined && x.name) arr.push(x.name));
这对我的口味来说还不够实用,不过……这里的
arr
有一个副作用。因此,一种更好的通用方法(IMO)是允许一个上下文对象(或者一个累加器,如果你愿意的话,一个la
数组#reduce
):

现在你可以这样称呼它,没有副作用:

const arr = walk(structure, (x, context) => {
  if(x !== undefined && x.name) context.push(x.name);
}, []);

您可以迭代该对象,并为数组或对象再次调用它。然后拿到想要的财产

“严格使用”;
函数getProperty(对象、键){
功能iter(a){
var项目=本?本[a]:a;
if(此&&a==键){
返回结果。推送(项目);
}
if(阵列isArray(项目)){
返回项目。forEach(iter);
}
if(item!==null&&typeof item==='object'){
返回对象.key(item).forEach(iter,item);
}
}
var结果=[];
key(Object).forEach(iter,Object);
返回结果;
}
var结构={name:'alpha',数组:[{name:'beta'},{name:'gamma'}],对象:{name:'delta',数组:[{name:'epsilon'}]};
log(getProperty(结构,'name')

.as控制台包装{max height:100%!important;top:0;}
这可以通过下面的mixin优雅地完成,它是
..toPairs
的递归版本:

_.mixin({
    toPairsDeep: obj => _.flatMap(
        _.toPairs(obj), ([k, v]) =>
            _.isObjectLike(v) ? _.toPairsDeep(v) : [[k, v]])
});
然后,要获得您想要的结果:

result = _(structure)
    .toPairsDeep()
    .map(1)
    .value()
如果存在除
名称
以外的标量属性,则必须将其过滤掉:

result = _(structure)
    .toPairsDeep()
    .filter(([k, v]) => k === 'name')
    .map(1)
    .value()

使用
递归迭代对象。reduce()

函数getPropertyRecursive(obj,prop){ 返回还原(对象、函数(结果、值、键){ 如果(键===prop){ 结果:推(值); }else if(u.isObjectLike(值)){ 返回result.concat(getPropertyRecursive(value,prop)); } 返回结果; }, []); } 变量结构={ 名称:“alpha”, 数组:[{ 名称:“beta” }, { 名称:“伽马” }], 对象:{ 名称:“delta”, 数组:[{ 名称:“ε” }] } }; var result=getPropertyRecursive(结构“name”); 控制台日志(结果)
根据答案(),这里是mixin的另一个想法:

_.mixin({
  extractLeaves: (obj, filter, subnode, subpathKey, rootPath, pathSeparator) => {
    var filterKv = _(filter).toPairs().flatMap().value()
    var arr = _.isArray(obj) ? obj : [obj]
    return _.flatMap(arr, (v, k) => {
      if (v[filterKv[0]] === filterKv[1]) {
        var vClone = _.clone(v)
        delete vClone[subnode]
        vClone._absolutePath = rootPath + pathSeparator + vClone[subpathKey]
        return vClone
      } else {
        var newRootPath = rootPath
        if (_.isArray(obj)) {
          newRootPath = rootPath + pathSeparator + v[subpathKey]
        }  
        return _.extractLeaves(
          v[subnode], filter, subnode, 
          subpathKey, newRootPath, pathSeparator
        )
      } 
    })
  }
});
此示例适用于JSON,您希望在其中提取叶节点:

{
    "name": "raka",
    "type": "dir",   
    "children": [{
        "name": "riki",
        "type": "dir",
        "children": [{
            "name": "roko",
            "type": "file"
        }]
    }]
}
这样使用它:

_.extractLeaves(result, {type: "file"}, "children", "name", "/myHome/raka", "/")
您将获得:

[
  {
    "name": "roko",
    "type": "file",
    "_absolutePath": "/myHome/raka/riki/roko"
  }
]

这里有很多非常好和有价值的答案。我接受了这个,因为它最接近我在问题中提到的
lodash
。谢谢大家!
[
  {
    "name": "roko",
    "type": "file",
    "_absolutePath": "/myHome/raka/riki/roko"
  }
]