如何在Javascript中基于多个数组属性取消分组对象数组

4

想象一个对象数组,每个对象都有一些数组属性,比如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内置函数实现。这就是我要求的。


1
我所知道的没有本地方法:我猜这是因为你看到的本质上是创建版本+目标的所有可能组合。这将需要一个基本的“for”循环,并且随着维度越多,它会变得更加复杂,因为嵌套的循环会越来越多。 - Terry
4个回答

6
你可以使用 .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) - adiga
关于生成器函数的一点说明:函数中执行递归的最后一行命令会让Typescript抱怨“期望3个参数,但只提供了1个或更多.ts(2556) | 未提供'key'的参数”。不知道是Typescript不支持这种表示法还是其他原因。总之,这个问题与Typescript无关。 - vahdet
@adiga 不,这是一个迭代器,而且据我所知 flatMap 可以处理它们。不过你说的参数是正确的。 - Jonas Wilms
@vahdet nah,根本原因是我的失误,这个编辑可能会起作用。但是TypeScript可能无法正确地对其进行类型标注,它需要注释。 - Jonas Wilms

0

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" } - vahdet

0
我们可以按照以下方式取消对象的分组。

const rows = [
  {
"groupId": "ff686b1c-0d83-4e9e-ac0e-edd4ed7a1579",
"clientName": "Apple",
"Id": 110117,
"manageFunds": [
  {
    "accountId": "eb9e38a8-2e0e-46c2-b50a-fa5c7c18ea53",
    "address": {
      "address1": "",
      "city": "",
      "state": "",
      "zipcode": "",
      "country": ""
    }
  },
  {
    "accountId": "eb9e38a8-2e0e-46c2-b50a-fa5c7c18ea54",
    "address": {
      "address1": "",
      "city": "",
      "state": "",
      "zipcode": "",
      "country": ""
    }
  }
],
"size": 2,
"bulkUploadErrorMessage": ""
  },
  {
"groupId": "ff686b1c-0d83-4e9e-ac0e-edd4ed7a1589",
"clientName": "Mango",
"Id": 110118,
"manageFunds": [
  {
    "accountId": "eb9e38a8-2e0e-46c2-b50a-fa5c7c18ea55",
    "address": {
      "address1": "",
      "city": "",
      "state": "",
      "zipcode": "",
      "country": ""
    }
  },
  {
    "accountId": "eb9e38a8-2e0e-46c2-b50a-fa5c7c18ea56",
    "address": {
      "address1": "",
      "city": "",
      "state": "",
      "zipcode": "",
      "country": ""
    }
  }
],
"size": 2,
"bulkUploadErrorMessage": ""
  },
  {
"groupId": "ff686b1c-0d83-4e9e-ac0e-edd4ed7a1599",
"clientName": "Orange",
"Id": 110119,
"manageFunds": [
  {
    "accountId": "eb9e38a8-2e0e-46c2-b50a-fa5c7c18ea57",
    "address": {
      "address1": "",
      "city": "",
      "state": "",
      "zipcode": "",
      "country": ""
    }
  },
  {
    "accountId": "eb9e38a8-2e0e-46c2-b50a-fa5c7c18ea58",
    "address": {
      "address1": "",
      "city": "",
      "state": "",
      "zipcode": "",
      "country": ""
    }
  },
  {
    "accountId": "eb9e38a8-2e0e-46c2-b50a-fa5c7c18ea59",
    "address": {
      "address1": "",
      "city": "",
      "state": "",
      "zipcode": "",
      "country": ""
    }
  }
],
"size": 3,
"bulkUploadErrorMessage": ""
  }
]

const mergeMF = input => {
const initObj = []
input.forEach(rootItem => {
    const { Id, clientName, manageFunds } = rootItem
    const groupId = rootItem.groupId
    manageFunds.forEach(subItem => {
        const newSubItem = {
            ...subItem,
            accountId: subItem.accountId,
            Id: Id,
            clientName: clientName,
            groupId: groupId,
        }
        initObj.push({
            checked: false,
            data: newSubItem,
            isCollapsed: false,
            validationErrors: subItem.errorMessages
        })
    })
})
return initObj
}

console.log(mergeMF(rows))


0
你可以使用 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);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接