[英]Transform a generic JS collection to an object with specific keys and grouped values
我想创建一个JS函数,将通用集合(数组或对象的嵌套级别的数组或对象)转换为一个对象,该对象的键是输入数组参数(如果提供的话,如果输入中不是所有不同的对象键,则提供)和value是输入中这些键的叶值数组)。 如果提供了标志,则叶值可以是不同的。
就像是:
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);
要么
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 /下划线帮助器。
谢谢。
这个问题的主要部分是递归地遍历对象树并收集值。
为此,我们编写了一个函数,该函数查看一个值并确定它是否是对象。 如果不是,则将值连接到结果数组。 如果是这样,我们将使用相同的逻辑来减小其值。
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]" };
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.