1

I'm looking for reduce elements of array to one object by same key (id).
I try to use Array.reduce, but cannot reduce same id elements.
Actually I don't really full understand Array.reduce's mechanism... :(
Can anybody help me?

const arr = [{id: 1, value: 1, key: 'email'}, {id: 1, value: 1, key: 'status'}, {id: 2, value: 2, key: 'email'}, {id: 2, value: 2, key: 'status'}];

// EXPECTED
const arr = [{id: 1, data: {'email': 1, 'status': 1}}, {id: 2, data: {'email': 2, 'status': 2}}];

// WHAT I DO
const result = arr.reduce((acc, cur)=>{
  const {id, key, value} = cur;
  acc.forEach(el => {
    if (el.id === id) {
      el.data[key] = value;
    }
  })
  acc = [...acc, {'id': id, 'data': { [key]: value }}];
  
  return acc
}, [])

// WHAT I GET
[
  { id: 1, data: { email: 1, status: 1 } },
  { id: 1, data: { status: 1 } },
  { id: 2, data: { email: 2, status: 2 } },
  { id: 2, data: { status: 2 } }
]
hello
  • 131
  • 1
  • 10
  • Does this answer your question? [Most efficient method to groupby on an array of objects](https://stackoverflow.com/questions/14446511/most-efficient-method-to-groupby-on-an-array-of-objects) – derpirscher Apr 08 '22 at 06:11

2 Answers2

3

Try this one

const arr = [{id: 1, value: 1, key: 'email'}, {id: 1, value: 1, key: 'status'}, {id: 2, value: 2, key: 'email'}, {id: 2, value: 2, key: 'status'}];

const result = arr.reduce((acc, {id, key, value}) => {
  const entry = acc.find(i => i.id === id);
  if (!entry) {
    acc.push({
      id, data: {
        [key]: value
      }
    });
  } else {
    entry.data[key] = value;
  }
  return acc;
}, []);

console.log(result);
ProDec
  • 5,390
  • 1
  • 3
  • 12
  • Note: using Array.find() inside Array.reduce() is going to be expensive for larger arrays. I would recommend constructing an object with the ID as the key, and using that to build your "reduced" output. You can later manipulate that object into whatever format you would like, but the end result would be much more performant. This is all theoretical of course. – sleepystar96 Apr 08 '22 at 06:11
3

You can use Array.prototype.reduce() this way,
By group them by id as an object and then use Object.values() to get the array that you wanted:

const arr = [
  {id: 1, value: 1, key: 'email'},
  {id: 1, value: 1, key: 'status'},
  {id: 2, value: 2, key: 'email'},
  {id: 2, value: 2, key: 'status'}
];

const arr2 = [
  {id: 1, data: {'email': 1, 'status': 1}},
  {id: 2, data: {'email': 2, 'status': 2}}
];

const res = Object.values(arr.reduce((acc, curr) => {
  if(!acc[curr.id]) {
    acc[curr.id] = {
      id: curr.id,
      data: {
        [curr.key]: curr.value
      }
    }
  } else {
    acc[curr.id].data = {
      ...acc[curr.id].data,
      [curr.key]: curr.value
    }
  }

  return acc;
}, {}));

console.log(res);
zb22
  • 3,126
  • 3
  • 19
  • 34