我一直在使用lodash一段时间,我真的很喜欢_.set
和_.get
方法。
我正在尝试解决一个问题,以获得最终值为字符串的深层键路径,但是当我对它太愚蠢时。花3个小时就找不到完美的解决方案:
const myObject = {
a: 'myObject.a',
b: {
ba: 'myObject.b.ba',
bb: ['myObject.b.bb[0]'],
},
c: [
{ ca: 'myObject.c[0].ca' },
],
};
所以我有myObject
(在现实生活中更加嵌套)我希望获得值的路径,但只有最后的路径。
该方法看起来像getDeepPaths(myObject)
,在这种情况下会返回:['myObject.a', 'myObject.b.ba', 'myObject.b.bb[0]', 'myObject.c[0].ca' ]
之前有人解决过这样的问题吗?
答案 0 :(得分:2)
递归实际上并不那么难。以下是解决此问题的方法:
const myObject = {
a: 'myObject.a',
b: {
ba: 'myObject.b.ba',
bb: ['myObject.b.bb[0]'],
},
c: [
{ ca: 'myObject.c[0].ca' },
],
};
var stringLeaves = function(path, obj) {
if (typeof obj === 'string') {
return [path]
}
return Object.keys(obj)
.filter(k => obj.hasOwnProperty(k))
.map(k => stringLeaves(path + '.' + k, obj[k]))
.reduce((a,x) => a.concat(x), []); // this line flattens the array
};
console.log(stringLeaves('myObject', myObject));

工作由stringLeaves
功能完成。在这个功能中:
obj
是一个字符串,则只返回当前路径。stringLeaves
。该函数的约定是它返回所有可能匹配的数组。这就是原因:
.reduce((a,x) => a.concat(x), []);
行:将数组数组转换为一个数组,该数组包含原始数组中的所有值。请注意,该函数无法推断出您的对象被称为myObject
,因此我将该名称作为初始路径传递。
答案 1 :(得分:0)
我将提供一个更通用的解决方案,它不使用lodash或其他外部依赖项
const traverse = function* (node, path = [])
{
if (Object (node) === node)
for (const [ key, value ] of Object.entries (node))
yield* traverse (value, [ ...path, key ])
else
yield [ path, node ]
}
我们可以使用for
循环轻松地逐步执行数据。请注意,生成器为原始对象中的每个值生成path
- value
对。 所有原语值都包含在输出中,而不仅仅是字符串
// add a non-string value for demo
const myObject = {
...
d: 1
};
for (const [ path, value ] of traverse (myObject)) {
console.log ('path', path)
console.log ('value', value)
console.log ('---')
}
// path [ 'a' ]
// value myObject.a
// ---
// path [ 'b', 'ba' ]
// value myObject.b.ba
// ---
// path [ 'b', 'bb', '0' ]
// value myObject.b.bb[0]
// ---
// path [ 'c', '0', 'ca' ]
// value myObject.c[0].ca
// ---
// path [ 'd' ]
// value 1
// ---
如果我们愿意,我们可以使用Array.from
Array.from (traverse (myObject))
// [ [ [ 'a' ], 'myObject.a' ]
// , [ [ 'b', 'ba' ], 'myObject.b.ba' ]
// , [ [ 'b', 'bb', '0' ], 'myObject.b.bb[0]' ]
// , [ [ 'c', '0', 'ca' ], 'myObject.c[0].ca' ]
// , [ [ 'd' ], 1 ]
// ]
您可能已经注意到,我将path
保留为数组,而不是将其设为.
- 分隔的字符串。没有必要把它变成一个字符串,只是为了以后再分开它。
const lookup = (obj, [ key, ...path ]) =>
obj && key
? lookup (obj [key], path)
: obj
for (const [ path, value ] of traverse (myObject)) {
console.log ('path', path)
console.log ('value', value)
console.log ('lookup', lookup (myObject, path))
console.log ('---')
}
// path [ 'a' ]
// value myObject.a
// lookup myObject.a
// ---
// path [ 'b', 'ba' ]
// value myObject.b.ba
// lookup myObject.b.ba
// ---
// path [ 'b', 'bb', '0' ]
// value myObject.b.bb[0]
// lookup myObject.b.bb[0]
// ---
// path [ 'c', '0', 'ca' ]
// value myObject.c[0].ca
// lookup myObject.c[0].ca
// ---
// path [ 'd' ]
// value 1
// lookup 1
// ---