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"
}
]