Reformat an object array by grouping by one common element

116 views Asked by At

I have the following array:

var a = [
  { "box":"1", "product":"Pen", "color":"White" },
  { "box":"1", "product":"Pencil", "color":"Blue" },
  { "box":"1", "product":"Marker", "color":"Red" },
  { "box":"2", "product":"Paper", "color":"White"},
  { "box":"2", "product":"GlossPaper", "color":"Yello"},
  { "box":"3", "product":"Eraser", "color":"Pink"}
];

I would like to reformat it to look like:


a = {
    "box": [
        {
            "number": "1", "contents": [
                {"product": "Pen", "color": "White"},
                {"product": "Pencil", "color": "Blue"},
                {"product": "Marker", "color": "Red"}
            ]
        },
        {
            "number": "2", "contents": [
                {"product": "Paper", "color": "White"},
                {"product": "GlossPaper", "color": "Yellow"}
            ]
        },
        {
            "number": "3", "contents": [
                {"product": "Eraser", "color": "Pink"}
            ]
        }
    ]
}

I was experimenting with reduce and tried:

a = a.reduce(function(x, e) {
  var estKey = (e['box']);
  (x[estKey] ? x[estKey] : (x[estKey] = null || [])).push(e);
  return x;
}, {});

but this doesn't produce the desired format. As you can tell, I'm introducing new properties number and contents

5

There are 5 answers

0
Selaka Nanayakkara On

const originalArray = [
  { "box":"1", "product":"Pen", "color":"White" },
  { "box":"1", "product":"Pencil", "color":"Blue" },
  { "box":"1", "product":"Marker", "color":"Red" },
  { "box":"2", "product":"Paper", "color":"White"},
  { "box":"2", "product":"GlossPaper", "color":"Yello"},
  { "box":"3", "product":"Eraser", "color":"Pink"}
];

const reformattedObject = {};

originalArray.forEach(item => {
  const boxNumber = item.box;
  const product = item.product;
  const color = item.color;

  if (!reformattedObject[boxNumber]) {
    reformattedObject[boxNumber] = {
      number: boxNumber,
      contents: []
    };
  }

  reformattedObject[boxNumber].contents.push({
    product: product,
    color: color
  });
});

const finalResult = {
  box: Object.values(reformattedObject)
};

console.log(JSON.stringify(finalResult, null, 2));

0
gog On

You're looking for Object.groupBy

let groups = Object.groupBy(yourArray, obj => obj.box)
let result = Object.entries(groups)
    .map(([number, contents]) => ({number, contents}))
1
Andrew Parks On

Since Object.groupBy is only available in very recent browsers, you can use reduce like this:

const before = [
  { "box":"1", "product":"Pen", "color":"White" },
  { "box":"1", "product":"Pencil", "color":"Blue" },
  { "box":"1", "product":"Marker", "color":"Red" },
  { "box":"2", "product":"Paper", "color":"White"},
  { "box":"2", "product":"GlossPaper", "color":"Yello"},
  { "box":"3", "product":"Eraser", "color":"Pink"}
];

const after = {
  box: Object.values(before.reduce((a, {box: n, product, color}) => (
    (a[n] ??= {number: n, contents:[]}).contents.push({product, color}), a), 
    {}
  ))
}

console.log(after)

0
Twister On

const data = [
  { "box":"1", "product":"Pen", "color":"White" },
  { "box":"1", "product":"Pencil", "color":"Blue" },
  { "box":"1", "product":"Marker", "color":"Red" },
  { "box":"2", "product":"Paper", "color":"White"},
  { "box":"2", "product":"GlossPaper", "color":"Yello"},
  { "box":"3", "product":"Eraser", "color":"Pink"}
];

const boxes = Object.groupBy(data, obj => obj.box);

const finalResult = { 
  box: Object.keys(boxes)
    .map((key, index) => (
      {number: index+1, 
      contents: boxes[key]
                .map(({ product, color}) => ({ product, color}))})
    )};
    
console.log("finalResult", finalResult);

3
Alexander Nenashev On

A performant one-liner:

var a = [
  { "box":"1", "product":"Pen", "color":"White" },
  { "box":"1", "product":"Pencil", "color":"Blue" },
  { "box":"1", "product":"Marker", "color":"Red" },
  { "box":"2", "product":"Paper", "color":"White"},
  { "box":"2", "product":"GlossPaper", "color":"Yello"},
  { "box":"3", "product":"Eraser", "color":"Pink"}
];

const result = {box: a.reduce((r, {box:number, product, color}) => ((r.map[number] ??= r.arr[r.arr.length] = {number, contents:[]}).contents.push({product, color}), r), {arr:[], map:{}}).arr};


console.log(result);

` Chrome/119
----------------------------------------------------------
Alexander      1.00x  |  x1000000  126  128  134  138  143
Andrew Parks   1.33x  |  x1000000  168  169  172  177  181
Twister        2.71x  |  x1000000  342  354  358  362  392
----------------------------------------------------------
https://github.com/silentmantra/benchmark `

var a = [
  { "box":"1", "product":"Pen", "color":"White" },
  { "box":"1", "product":"Pencil", "color":"Blue" },
  { "box":"1", "product":"Marker", "color":"Red" },
  { "box":"2", "product":"Paper", "color":"White"},
  { "box":"2", "product":"GlossPaper", "color":"Yello"},
  { "box":"3", "product":"Eraser", "color":"Pink"}
];

// @benchmark Andrew Parks
{
  box: Object.values(a.reduce((a, {box: n, product, color}) => (
    (a[n] ??= {number: n, contents:[]}).contents.push({product, color}), a), 
    {}
  ))
}
// @benchmark Twister
const boxes = Object.groupBy(a, obj => obj.box);
{ 
  box: Object.keys(boxes)
    .map((key, index) => (
      {number: index+1, 
      contents: boxes[key]
                .map(({ product, color}) => ({ product, color}))})
    )};
    
// @benchmark Alexander
{box: a.reduce((r, {box:number, product, color}) => ((r.map[number] ??= r.arr[r.arr.length] = {number, contents:[]}).contents.push({product, color}), r), {arr:[], map:{}}).arr};

/*@end*/eval(atob('e2xldCBlPWRvY3VtZW50LmJvZHkucXVlcnlTZWxlY3Rvcigic2NyaXB0Iik7aWYoIWUubWF0Y2hlcygiW2JlbmNobWFya10iKSl7bGV0IHQ9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7dC5zcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9naC9zaWxlbnRtYW50cmEvYmVuY2htYXJrL2xvYWRlci5qcyIsdC5kZWZlcj0hMCxkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHQpfX0='));