我想创建一个JS函数,将一个泛型集合(数组或对象的嵌套级别的数组或对象)转换为一个对象,其键是一个输入数组参数(如果提供的话,如果不是所有不同的对象键)在输入中)和值是输入中这些键的叶值数组。如果提供标记,叶子值可以是不同的。
类似的东西:
transform([{a:1, b:'1', c:true, d:1},{a:'1', b:2, c:3, d:false}, {a:1, c:'test'}], ['a','b','c'], true);
OR
transform({x:{b:2,c:{c:3, d:1}},b:'1',z:{b:2,c:true,a:1},a:'1',g:{c:'test',d:false}}, ['a','b','c'], true);
将产生相同的输出:
{a:[1, '1'], b:['1', 2], c:[true, 3, 'test']}
如果省略第二个参数。它会产生这个:
{a:[1, '1'], b:['1', 2], c:[true, 3, 'test'], d:[1,false]}
快速和/或优雅的方式是什么?
是否有任何lodash / undercore帮助器。
感谢。
答案 0 :(得分:0)
这个问题的主要部分是递归地遍历对象树并收集值。
为此,我们编写一个查看值的函数,并确定它是否为对象。如果不是,我们将值连接到结果数组。如果是,我们使用相同的逻辑减少其值。
const deepValues = (obj, res = []) =>
isObject(obj)
? Object.values(obj).reduce(flip(deepValues), res)
: res.concat(obj)
console.log(
deepValues({x:{b:2,c:{c:3, d:1}}})
);
// Utils
function flip(f) { return (x, y) => f(y, x) };
function isObject(x) { return Object.prototype.toString.call(x) === "[object Object]" };
一旦你理解了这个功能,添加其他部分就会更有意义。
因此,如果其值与我们的标准匹配,我们希望将值连接到结果。
我们在签名中添加了keyPred
函数,并开始使用[key, value]
对,而不仅仅是值。谓词接受一个键并返回一个布尔值,指示我们是否应该存储它。
这个函数变得有点难以阅读,但它与初始函数没有太大区别:
const certainDeepValues = (keyPred, obj, res = []) =>
Object.entries(obj)
.reduce((r, [k, v]) =>
isObject(v)
? certainDeepValues(keyPred, v, r)
: keyPred(k) ? r.concat(v) : r
, res);
console.log(
certainDeepValues(
k => k === "c",
{x:{b:2,c:{c:3, d:1}}}
)
);
// Utils
function isObject(x) { return Object.prototype.toString.call(x) === "[object Object]" };
Javascript具有完美的类型,可用于一组唯一值:Set
,这使得这个变化很小。默认参数为new Set
,concat
方法为add
。
const certainDeepValues = (keyPred, obj, res = new Set()) =>
Object.entries(obj)
.reduce((r, [k, v]) =>
isObject(v)
? certainDeepValues(keyPred, v, r)
: keyPred(k) ? r.add(v) : r
, res);
console.log(
[...certainDeepValues(
k => k === "c",
{x:{b:2,c:{c:3, d:1, e: { c: "2" }}}, c: 3}
)]
);
// Utils
function flip(f) { return (x, y) => f(y, x) };
function isObject(x) { return Object.prototype.toString.call(x) === "[object Object]" };
现在,剩下的就是实现所需的签名。这是它有点丑陋的地方,但由于你没有发布任何尝试,只是把你的“问题”作为一个任务写给我们,我会留给你清理它:)
const certainDeepValues =
(concat, keyPred, obj, res = new Set()) =>
Object.entries(obj)
.reduce((r, [k, v]) =>
isObject(v)
? certainDeepValues(concat, keyPred, v, r)
: keyPred(k) ? concat(r, k, v) : r
, res);
const transform = (xs = [], keys = [], uniquesOnly = false) => {
const keySet = new Set(keys);
const keyPred = keys.length
? k => keySet.has(k)
: k => true;
const concat = uniquesOnly
? (acc, k, v) => (acc[k] = (acc[k] || new Set()).add(v), acc)
: (acc, k, v) => (acc[k] = (acc[k] || []).concat(v), acc)
const unwrap = uniquesOnly
? acc => mapObj(s => [...s], acc)
: acc => acc;
return unwrap(xs.reduce(
(r, x) => certainDeepValues(concat, keyPred, x, r),
{}
));
};
console.log(
JSON.stringify(
transform([{a:1, b:'1', c:true, d:1},{a:'1', b:2, c:3, d:false}, {a:1, c:'test'}], ['a','b','c'], true)
)
);
console.log(
JSON.stringify(
transform([{x:{b:2,c:{c:3, d:1}},b:'1',z:{b:2,c:true,a:1},a:'1',g:{c:'test',d:false}}], ['a','b','c'], true)
)
);
console.log(
JSON.stringify(
transform([{x:{b:2,c:{c:3, d:1}},b:'1',z:{b:2,c:true,a:1},a:'1',g:{c:'test',d:false}}], undefined, true)
)
);
// Utils
function mapObj(f, obj) { return Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: f(v)}))); }
function flip(f) { return (x, y) => f(y, x) };
function isObject(x) { return Object.prototype.toString.call(x) === "[object Object]" };