如何将对象数组转换为按属性分组的新对象数组?

IT技术 javascript reactjs ecmascript-6
2021-05-07 07:53:07

请参阅我的Fiddle,其中包含以下所有代码。

如果之前已经回答过这个问题,我深表歉意。我在这里找到了关于按属性分组的类似问题,但我没有找到结果仍然是对象数组的示例。

我从这种数据格式开始:

const originalData = [
  {
    "groupId": 0,
    "color": "red",
    "shape": "circle"
  },
  {
    "groupId": 1,
    "color": "green",
    "shape": "square"
  },
  {
    "groupId": 1,
    "color": "orange",
    "shape": "hexagon"
  },
  {
    "groupId": 1,
    "color": "purple",
    "shape": "triangle"
  },
  {
    "groupId": 2,
    "color": "aqua",
    "shape": "diamond"
  },
  {
    "groupId": 2,
    "color": "blue",
    "shape": "trapezoid"
  }
];

我想把它转换成一个新的对象数组,按groupId属性值分组

const desiredData = [
  {
    "groupId": 0,
    "items": [
      {
        "color": "red",
        "shape": "circle"
      }
    ]
  },
  {
    "groupId": 1,
    "items": [
      {
        "color": "green",
        "shape": "square"
      },
      {
        "color": "orange",
        "shape": "hexagon"
      },
      {
        "color": "purple",
        "shape": "triangle"
      }
    ]
  },
  {
    "groupId": 2,
    "items": [
      {
        "color": "aqua",
        "shape": "diamond"
      },
      {
        "color": "blue",
        "shape": "trapezoid"
      }
    ]
  }
];

这个 reduce 函数(我在MDN 上找到的)是我最接近转换数据的函数我在 Javascript 中转换数据的经验有限,我不确定如何group在转换过程中添加字段(如)。此外,结果是一个对象,而不是一个对象数组。

const actualFormattedData = originalData.reduce((acc, obj) => {
  let key = obj['groupId'];
  if (!acc[key]) {
    acc[key] = [];
  }
  acc[key].push(obj);
  return acc;
}, {});

reduce 函数的输出:

{
  "0": [
    {
      "groupId": 0,
      "color": "red",
      "shape": "circle"
    }
  ],
  "1": [
    {
      "groupId": 1,
      "color": "green",
      "shape": "square"
    },
    {
      "groupId": 1,
      "color": "orange",
      "shape": "hexagon"
    },
    {
      "groupId": 1,
      "color": "purple",
      "shape": "triangle"
    }
  ],
  "2": [
    {
      "groupId": 2,
      "color": "aqua",
      "shape": "diamond"
    },
    {
      "groupId": 2,
      "color": "blue",
      "shape": "trapezoid"
    }
  ]
}

最终目标是在 React 中映射对象数组。我知道我可以使用Object.entries和 数组索引来实现与actualFormattedData原样类似的结果,但如果我能首先使actualFormattedData看起来完全像desiredData.

4个回答

这应该有效:

const dict = originalData.reduce((acc, obj) => {
  let groupId = obj['groupId'];
  delete obj.groupId;
  if (!acc[groupId]) {
    acc[groupId] = { // here is where we add the fields you wanted
        groupId,
        items: []
      };
  }
  acc[groupId].items.push(obj);
  return acc;
}, {});

// turn this into an array, just getting the values of the fields in the dictionary
const actualFormattedData = Object.values(dict);

这是你的小提琴解决方案

https://jsfiddle.net/07n9ks86/

以及它的关键代码(n2):

const formattedData = originalData.reduce((acc, curr) => {
  console.log(acc)
  const index = acc.findIndex(x => x.group === curr.group);
  if (index > 0) {
    acc[index] = {
      ...acc[index],
      items: [...acc[index].items,
        {
          'color': curr.color,
          'shape': curr.shape
        }
      ]
    }
  } else {
    acc.push({
      group: curr.group,
      items: [{
        'color': curr.color,
        'shape': curr.shape
      }]
    })
  }
  return acc;
}, []);

一个简单的解决方案可以通过对 的一次调用来实现Array#reduce(),如下面的代码片段中详述。

请注意,此解决方案强调简单而不是效率,并且往往不适合非常大的输入数组:

const originalData=[{groupId:0,color:"red",shape:"circle"},{groupId:1,color:"green",shape:"square"},{groupId:1,color:"orange",shape:"hexagon"},{groupId:1,color:"purple",shape:"triangle"},{groupId:2,color:"aqua",shape:"diamond"},{groupId:2,color:"blue",shape:"trapezoid"}];

/* Use reduce to iterate and transform originalData array to desired result */
const desiredData = originalData.reduce((result, item) => {
  
  /* The group item to add from this iteration */
  const groupItem = { color : item.color, shape : item.shape };
  
  /* Search for item that already exists with matching group id */
  const existingGroup = result.find(resultItem => resultItem.groupId === item.groupId);
  if(existingGroup) {
    /* Add item to group if found */
    existingGroup.items.push(groupItem);
  }
  else {
    /* Add group with item if not group found */
    result.push({ 
      groupId : item.groupId,
      items : [ groupItem ]
    });
  }
  
  return result;
  
}, []);

console.log(desiredData);

希望有帮助!

另一种按属性名称分组的最简单方法是使用 lodash。

let groupedData = _.groupBy(rawData, dataObj => dataObj.propertyToGroupBy)

groupedData您要查找的结果在哪里rawData是原始数据,propertyToGroupBy还是要与之分组的对象的属性。

你可以检查这个答案。