【问题标题】:How to ungroup an object array based on multiple array properties in Javascript如何根据Javascript中的多个数组属性取消组合对象数组
【发布时间】:2019-12-18 07:36:48
【问题描述】:

想象一个对象数组,每个对象都有特定的数组属性,例如versionstargets。我想为每个versiontarget 取消组合对象。

const myArray = [
  { 'name': 'a', versions: [1, 2], targets: ['server1', 'server2']},
  { 'name': 'b', versions: [], targets: ['server1', 'server2', 'server3']},
  { 'name': 'c', versions: [1], targets: []}
]

上述myArray 的期望输出为:

[
  { 'name': 'a', version: 1,         target: 'server1'},
  { 'name': 'a', version: 1,         target: 'server2'},
  { 'name': 'a', version: 2,         target: 'server1'},
  { 'name': 'a', version: 2,         target: 'server2'},
  { 'name': 'b', version: undefined, target: 'server1'},
  { 'name': 'b', version: undefined, target: 'server2'},
  { 'name': 'b', version: undefined, target: 'server3'},
  { 'name': 'c', version: 1,         target: undefined},
]

我考虑过使用嵌套的for/forEach 循环,但我几乎可以肯定必须有一个更精确和合理的方法来实现它内置的 es6 函数左右。这就是我所要求的。

【问题讨论】:

  • 我没有现成的本地方法:我想这是因为您所看到的本质上是创建版本+目标的所有可能组合。这将需要一个基本的for 循环,并且您拥有的维度越多,它的复杂性就会增加,因为循环必须越嵌套。

标签: javascript arrays ecmascript-6


【解决方案1】:

你可以使用.flatMap:

  const notEmpty = arr => arr.length ? arr : [undefined];

  myArray.flatMap(({ name, versions, targets }) => notEmpty(versions).flatMap(version => notEmpty(targets).map(target => ({ name, version, target }))));

或者更多维度,生成器变得非常有用:

  function* cartesian(obj, key, ...keys) {
     if(!key) {
         yield obj;
         return;
     }

    const { [key + "s"]: entries, ...rest } = obj;
    for(const entry of (entries.length ? entries : [undefined])) {
        yield* cartesian({ [key]: entry, ...rest }, ...keys);
     }
 }

 myArray.flatMap(it => cartesian(it, "version", "target"))

【讨论】:

  • 不应该是it => Array.from(cartesian(...))吗?目前它返回一个生成器对象。另外,这里的其余参数语法:cartesian(obj, key, ...keys)
  • 关于生成器函数方式的一个简单说明:函数中执行递归的最后一个命令行使 Typescript 像Expected 3 arguments, but got 1 or more.ts(2556) | An argument for 'key' was not provided 一样抱怨。不知道是不是 Typescript 不支持这种表示法。总而言之,这个问题没有 Typescript。
  • @adiga 不,它是一个迭代器,afaik flatMap 可以处理这些。哦,不过你的参数是对的
  • @vahdet 不,根本原因是我的错误,这可能适用于编辑。但是 Typescript 可能无法正确输入,需要注释。
【解决方案2】:

你可以使用reduce方法:

const result = myArray.reduce((a, {name, versions, targets}) => {
  versions.forEach((el, ind) => {
      a.push({name, version: versions[ind], target: targets[ind]})
  });
  targets.forEach((el, ind) => {
      a.push({name, version: versions[ind], target: targets[ind]})
  });  
  return a;    
}, []);

一个例子:

const myArray = [
  { 'name': 'a', versions: [1, 2], targets: ['server1', 'server2']},
  { 'name': 'b', versions: [], targets: ['server1', 'server2', 'server3']},
  { 'name': 'c', versions: [1], targets: []}
]


const result = myArray.reduce((a, {name, versions, targets}) => {
  versions.forEach((el, ind) => {
      a.push({name, version: versions[ind], target: targets[ind]})
  });
  targets.forEach((el, ind) => {
      a.push({name, version: versions[ind], target: targets[ind]})
 });  
  return a;
}, []);

console.log(result);

【讨论】:

    【解决方案3】:

    const myArray = [
      { 'name': 'a', versions: [1, 2], targets: ['server1', 'server2'] },
      { 'name': 'b', versions: [], targets: ['server1', 'server2', 'server3'] },
      { 'name': 'c', versions: [1], targets: [] }
    ]
    
    const reducer = (acc, current) => {
      const { name, versions, targets } = current;
      for (let i = 0; i < versions.length; i++) {
        acc.push({ name, version: versions[i], target: targets[i] })
      }
    
      for (let i = 0; i < targets.length; i++) {
        acc.push({ name, version: versions[i], target: targets[i] })
      }
    
      return acc;
    }
    
    const ungroup = array => array.reduce(reducer, [])
    
    console.log(ungroup(myArray))

    【讨论】:

    • 它的结果包含像{ "name": "a", "version": 1, "target": "server1" }这样的重复项
    猜你喜欢
    • 2019-09-09
    • 1970-01-01
    • 2018-07-12
    • 1970-01-01
    • 2014-08-05
    • 1970-01-01
    • 2021-04-09
    • 2015-09-23
    • 1970-01-01
    相关资源
    最近更新 更多