6

I guess I have a dead simple problem but still didn't find a solution. I have an array which looks like this:

var originalArray = [{
  id: 1,
  elements: [1, 2]
},
{
  id: 1,
  elements: [3, 4]
},
{
  id: 5,
  elements: ['a', 'b']
},
{
  id: 5,
  elements: ['c', 'd']
}, {
  id: 27,
  elements: []
}]

I'd like to modify it to look like this (merge by id and join elements):

newArray = [{
  id: 1,
  elements: [1, 2, 3, 4]
}, {
  id: 5,
  elements: ['a', 'b', 'c', 'd']
}, {
  id: 27,
  elements: []
}]

I already had multiple tries but still didn't find an elegant way of doing it.

michithebest
  • 85
  • 1
  • 1
  • 8
  • 6
    Hi! Please take the [tour] (you get a badge!) and read through the [help], in particular [*How do I ask a good question?*](/help/how-to-ask) Your best bet here is to do your research, [search](/help/searching) for related topics on SO, and give it a go. ***If*** you get stuck and can't get unstuck after doing more research and searching, post a [mcve] of your attempt and say specifically where you're stuck. People will be glad to help. – T.J. Crowder Nov 04 '19 at 11:24
  • So by looking at the solutions below, it was not _dead_ simple ;) – DauleDK Nov 04 '19 at 14:20

7 Answers7

8

You can create an object keyed by ID and push elements with the same ID to them, then convert back to an array. This is more efficient than looping through on every iteration for larger arrays:

var originalArray = [{
  id: 1,
  elements: [1, 2]
},
{
  id: 1,
  elements: [3, 4]
},
{
  id: 5,
  elements: ['a', 'b']
},
{
  id: 5,
  elements: ['c', 'd']
}, {
  id: 27,
  elements: []
}];

const arrayHashmap = originalArray.reduce((obj, item) => {
  obj[item.id] ? obj[item.id].elements.push(...item.elements) : (obj[item.id] = { ...item });
  return obj;
}, {});

const mergedArray = Object.values(arrayHashmap);

console.log(mergedArray);
Dominic
  • 62,658
  • 20
  • 139
  • 163
  • why use ... in javaScript – lalit bhakuni Nov 05 '19 at 06:21
  • The `...` in push is necessary if passing multiple items, or you could use `concat(item.elements)` but would need to assign elements to it as it returns a new array. The `...item` is just common practice to "break" away from the original array so that (shallow) modifications to the object will not change the original array - generally this style of immutable coding causes less unexpected side effects and also makes object equality comparison much more efficient and easy e.g. https://reactjs.org/docs/react-api.html#reactpurecomponent is made possible by it – Dominic Nov 05 '19 at 09:14
3

Try this code :

var originalArray = [{
      id: 1,
      elements: [1, 2]
    },
    {
      id: 1,
      elements: [3, 4]
    },
    {
      id: 5,
      elements: ['a', 'b']
    },
    {
      id: 5,
      elements: ['c', 'd']
    }, {
      id: 27,
      elements: []
    }]
    var newArray = [];
    originalArray.forEach(item => {
       var newItem = {id: item.id, elements: []};
       originalArray.forEach(innerItem => {
          if(innerItem.id == item.id){
              newItem.elements = newItem.elements.concat(innerItem.elements);
          }
       });
      newArray.push(newItem);
    });
    console.log(newArray);

Output :

[{
  id: 1,
  elements: [1, 2, 3, 4]
}, {
  id: 5,
  elements: ['a', 'b', 'c', 'd']
}, {
  id: 27,
  elements: []
}]
Ilia Afzali
  • 429
  • 2
  • 6
2

You can use Array.prototype.reduce() to create an object with the ids as properties and the Object.values() to get the result:

const originalArray = [{id: 1, elements: [1, 2]}, {id: 1, elements: [3, 4]}, {id: 5, elements: ['a', 'b']}, {id: 5, elements: ['c', 'd']}, {id: 27, elements: []}]
const objIds = originalArray.reduce((a, { id, elements }) => {
  a[id] = a[id] || {id, elements: []}
  return {...a, ...{[id]: {id, elements: a[id].elements.concat(elements)}}}
}, {})
const result = Object.values(objIds)

console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Yosvel Quintero
  • 18,669
  • 5
  • 37
  • 46
1

You can do this with a reduce function:

[{
  id: 1,
  elements: [1, 2]
},
{
  id: 1,
  elements: [3, 4]
},
{
  id: 5,
  elements: ['a', 'b']
},
{
  id: 5,
  elements: ['c', 'd']
}, {
  id: 27,
  elements: []
}].reduce((prev, cur) => {
    const index = prev.findIndex(v => v.id === cur.id);
    if (index === -1) {
       prev.push(cur);
    } else {
        prev[index].elements.push(...cur.elements);
    }
    return prev;
}, [])
DauleDK
  • 3,313
  • 11
  • 55
  • 98
1

This will work and is also decently easy to understand. Firstly we check if the id is already in the newArray or not and we keep memory of this through a boolean outside the loop that we can verify later on. After this, if the id "space" is empty, we will fill it up, if it isn't then there is already an id there. Therefore, we need to update their elements. We can do this by firstly, grabbing the object in the new array that corresponds with the duplicate object in the initial array which has the same id. After this, we simply push each element from the duplicate to the new one.


var originalArray = [{
  id: 1,
  elements: [1, 2]
},
{
  id: 1,
  elements: [3, 4]
},
{
  id: 5,
  elements: ['a', 'b']
},
{
  id: 5,
  elements: ['c', 'd']
}, {
  id: 27,
  elements: []
}]

var newArray = [];
for (obj of originalArray) {

    var empty = true;
    for (newobj of newArray) {
       if (obj.id == newobj.id) { empty = false; }
    }
    if (empty) {
        newArray.push({id: obj.id, elements: obj.elements});
    } else {
        for (newobj of newArray) {
           if (newobj.id == obj.id) {
               for (o of obj.elements) {
                 newobj.elements.push(o);
                }
           }
        }
    }
}
console.log(newArray);
Sean
  • 767
  • 1
  • 6
  • 19
1

You could do this using reduce method and Map to store unique values for each id and then create an array using spread syntax ....

var data = [{"id":1,"elements":[1,2]},{"id":1,"elements":[3,4]},{"id":5,"elements":["a","b"]},{"id":5,"elements":["c","d"]},{"id":27,"elements":[]}]

const result = data.reduce((r, {id, elements}) => {
  if(r.get(id)) r.get(id).elements.push(...elements);
  else r.set(id, {id, elements});
  return r;
}, new Map).values();

console.log([...result])
Nenad Vracar
  • 118,580
  • 15
  • 151
  • 176
1

You can use nested for loops to do that.

var originalArray = [{
  id: 1,
  elements: [1, 2]
},
{
  id: 1,
  elements: [3, 4]
},
{
  id: 5,
  elements: ['a', 'b']
},
{
  id: 5,
  elements: ['c', 'd']
}, {
  id: 27,
  elements: []
}]


for(let i=0;i<originalArray.length;i++){
  let key = originalArray[i].id;
  for(let j=i+1;j<originalArray.length;j++){
    if(originalArray[j].id == key){
      originalArray[i].elements  = [...originalArray[i].elements,...originalArray[j].elements];
      delete originalArray.splice(j,1);
    }

  }
}

console.log(originalArray);
Akshay Bande
  • 2,491
  • 2
  • 12
  • 29