将通用JS集合转换为具有特定键和分组值的对象

问题描述 投票:0回答:1

我想创建一个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);

要么

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 /下划线帮助器。

谢谢。

javascript arrays object collections grouping
1个回答
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]" };

定制它

一旦理解了这个功能,添加其他部分就会更有意义。

Step 1: Not all the values...

因此,如果它的键与我们的标准匹配,我们希望将值连接到结果。

我们在签名中添加了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]" };

Step 2: Only collect unique values

Javascript具有完美的类型,可用于收集独特的值:Set,这使得这个变化很小。默认参数是new Setconcat方法变为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]" };
© www.soinside.com 2019 - 2024. All rights reserved.