Merge JavaScript objects in array with same key

JavascriptArrays

Javascript Problem Overview


What is the best way to merge array contents from JavaScript objects sharing a key in common?

How can array in the example below be reorganized into output? Here, all value keys (whether an array or not) are merged into all objects sharing the same name key.

var array = [    {        name: "foo1",        value: "val1"    }, {        name: "foo1",        value: [            "val2",            "val3"        ]
    }, {
        name: "foo2",
        value: "val4"
    }
];

var output = [    {        name: "foo1",        value: [            "val1",            "val2",            "val3"        ]
    }, {
        name: "foo2",
        value: [
            "val4"
        ]
    }
];

Javascript Solutions


Solution 1 - Javascript

Here is one option:-

var array = [{
  name: "foo1",
  value: "val1"
}, {
  name: "foo1",
  value: ["val2", "val3"]
}, {
  name: "foo2",
  value: "val4"
}];

var output = [];

array.forEach(function(item) {
  var existing = output.filter(function(v, i) {
    return v.name == item.name;
  });
  if (existing.length) {
    var existingIndex = output.indexOf(existing[0]);
    output[existingIndex].value = output[existingIndex].value.concat(item.value);
  } else {
    if (typeof item.value == 'string')
      item.value = [item.value];
    output.push(item);
  }
});

console.dir(output);

Solution 2 - Javascript

Here is another way of achieving that goal:

var array = [{
  name: "foo1",
  value: "val1"
}, {
  name: "foo1",
  value: [
    "val2",
    "val3"
  ]
}, {
  name: "foo2",
  value: "val4"
}];

var output = array.reduce(function(o, cur) {

  // Get the index of the key-value pair.
  var occurs = o.reduce(function(n, item, i) {
    return (item.name === cur.name) ? i : n;
  }, -1);

  // If the name is found,
  if (occurs >= 0) {

    // append the current value to its list of values.
    o[occurs].value = o[occurs].value.concat(cur.value);

    // Otherwise,
  } else {

    // add the current item to o (but make sure the value is an array).
    var obj = {
      name: cur.name,
      value: [cur.value]
    };
    o = o.concat([obj]);
  }

  return o;
}, []);

console.log(output);

Solution 3 - Javascript

Using lodash

var array = [{name:"foo1",value:"val1"},{name:"foo1",value:["val2","val3"]},{name:"foo2",value:"val4"}];

function mergeNames (arr) {
    return _.chain(arr).groupBy('name').mapValues(function (v) {
        return _.chain(v).pluck('value').flattenDeep();
    }).value();
}

console.log(mergeNames(array));

Solution 4 - Javascript

2021 version

var arrays = [{ name: "foo1",value: "val1" }, {name: "foo1", value: ["val2", "val3"] }, {name: "foo2",value: "val4"}];

const result = arrays.reduce((acc, {name, value}) => {
  acc[name] ??= {name: name, value: []};
  if(Array.isArray(value)) // if it's array type then concat 
    acc[name].value = acc[name].value.concat(value);
  else
    acc[name].value.push(value);
  
  return acc;
}, {});

console.log(Object.values(result));

Solution 5 - Javascript

Here is a version using an ES6 Map:

const arrays = [{ name: "foo1",value: "val1" }, {name: "foo1", value: ["val2", "val3"] }, {name: "foo2",value: "val4"}];

const map = new Map(arrays.map(({name, value}) => [name, { name, value: [] }])); 
for (let {name, value} of arrays) map.get(name).value.push(...[value].flat());
console.log([...map.values()]);

Solution 6 - Javascript

Try this:

var array = [{name:"foo1",value:"val1"},{name:"foo1",value:["val2","val3"]},{name:"foo2",value:"val4"},{name:"foo2",value:"val5"}];

for(var j=0;j<array.length;j++){
  var current = array[j];
  for(var i=j+1;i<array.length;i++){
    if(current.name = array[i].name){
      if(!isArray(current.value))
        current.value = [ current.value ];
      if(isArray(array[i].value))
         for(var v=0;v<array[i].value.length;v++)
           current.value.push(array[i].value[v]);
      else
        current.value.push(array[i].value);
      array.splice(i,1);
      i++;
    }
  }
}

function isArray(myArray) {
    return myArray.constructor.toString().indexOf("Array") > -1;
}

document.write(JSON.stringify(array));

Solution 7 - Javascript

Using reduce:

var mergedObj = array.reduce((acc, obj) => {
    if (acc[obj.name]) {
       acc[obj.name].value = acc[obj.name].value.isArray ? 
       acc[obj.name].value.concat(obj.value) : 
       [acc[obj.name].value].concat(obj.value);

    } else {
      acc[obj.name] = obj;
    }

    return acc;
}, {});

let output = [];
for (let prop in mergedObj) {
  output.push(mergedObj[prop]) 
}

Solution 8 - Javascript

> This work too !

      var array = [
        {
          name: "foo1",
          value: "val1",
        },
        {
          name: "foo1",
          value: ["val2", "val3"],
        },
        {
          name: "foo2",
          value: "val4",
        },
      ];
      let arr2 = [];
      array.forEach((element) => { // remove duplicate name
        let match = arr2.find((r) => r.name == element.name);
        if (match) {
        } else {
          arr2.push({ name: element.name, value: [] });
        }
      });
      arr2.map((item) => {
        array.map((e) => {
          if (e.name == item.name) {
            if (typeof e.value == "object") { //lets map if value is an object
              e.value.map((z) => {
                item.value.push(z);
              });
            } else {
              item.value.push(e.value);
            }
          }
        });
      });
      console.log(arr2);

Solution 9 - Javascript

Use lodash "uniqWith". As shown below
let _ = require("lodash");

var array = [
  { name: "foo1", value: "1" },
  { name: "foo1", value: "2" },
  { name: "foo2", value: "3" },
  { name: "foo1", value: "4" }
];

let merged = _.uniqWith(array, (pre, cur) => {
  if (pre.name == cur.name) {
    cur.value = cur.value + "," + pre.value;
    return true;
  }
  return false;
});

console.log(merged);
// output:  [{ name: "foo1", value: "1,2,4" }, { name: "foo2", value: "3" }];


Solution 10 - Javascript

const exampleObj = [{
  year: 2016,
  abd: 123
}, {
  year: 2016,
  abdc: 123
}, {
  year: 2017,
  abdcxc: 123
}, {
  year: 2017,
  abdcxcx: 123
}];
const listOfYears = [];
const finalObj = [];
exampleObj.map(sample => {    
  listOfYears.push(sample.year);
});
const uniqueList = [...new Set(listOfYears)];
uniqueList.map(list => {   
  finalObj.push({
    year: list
  });
});
exampleObj.map(sample => {    
  const sampleYear = sample.year;  
  finalObj.map((obj, index) => {     
    if (obj.year === sampleYear) {        
      finalObj[index] = Object.assign(sample, obj);       
    }  
  })
});

The final object be [{"year":2016,"abdc":123,"abd":123},{"year":2017,"abdcxcx":123,"abdcxc":123}]

Solution 11 - Javascript

It's been a while since this question was asked, but I thought I'd chime in as well. For functions like this that execute a basic function you'll want to use over and over, I prefer to avoid longer-written functions and loops if I can help it and develop the function as a one-liner using shallow Array.prototype functions like .map() and some other ES6+ goodies like Object.entries() and Object.fromEntries(). Combining all these, we can execute a function like this relatively easily.

First, I take in however many objects you pass to the function as a rest parameter and prepend that with an empty object we'll use to collect all the keys and values.

[{}, ...objs]

Next, I use the .map() Array prototype function paired with Object.entries() to loop through all the entries of each object, and any sub-array elements each contains and then either set the empty object's key to that value if it has not yet been declared, or I push the new values to the object key if it has been declared.

[{},...objs].map((e,i,a) => i ? Object.entries(e).map(f => (a[0][f[0]] ? a[0][f[0]].push(...([f[1]].flat())) : (a[0][f[0]] = [f[1]].flat()))) : e)[0]

Finally, to replace any single-element-arrays with their contained value, I run another .map() function on the result array using both Object.entries() and Object.fromEntries(), similar to how we did before.

let getMergedObjs = (...objs) => Object.fromEntries(Object.entries([{},...objs].map((e,i,a) => i ? Object.entries(e).map(f => (a[0][f[0]] ? a[0][f[0]].push(...([f[1]].flat())) : (a[0][f[0]] = [f[1]].flat()))) : e)[0]).map(e => e.map((f,i) => i ? (f.length > 1 ? f : f[0]) : f)));

This will leave you with the final merged object, exactly as you prescribed it.

let a = {
  a: [1,9],
  b: 1,
  c: 1
}
let b = {
  a: 2,
  b: 2
}
let c = {
  b: 3,
  c: 3,
  d: 5
}

let getMergedObjs = (...objs) => Object.fromEntries(Object.entries([{},...objs].map((e,i,a) => i ? Object.entries(e).map(f => (a[0][f[0]] ? a[0][f[0]].push(...([f[1]].flat())) : (a[0][f[0]] = [f[1]].flat()))) : e)[0]).map(e => e.map((f,i) => i ? (f.length > 1 ? f : f[0]) : f)));

getMergedObjs(a,b,c); // { a: [ 1, 9, 2 ], b: [ 1, 2, 3 ], c: [ 1, 3 ], d: 5 }

Solution 12 - Javascript

try this :

    var array = [
      {
          name: "foo1",
          value: "val1"
      }, {
          name: "foo1",
          value: [
              "val2",
              "val3"
          ]
      }, {
          name: "foo2",
          value: "val4"
      }
  ];
  
  var output = [
      {
          name: "foo1",
          value: [
              "val1",
              "val2",
              "val3"
          ]
      }, {
          name: "foo2",
          value: [
              "val4"
          ]
      }
  ];

  bb = Object.assign( {}, array, output );

console.log(bb) ; 

Solution 13 - Javascript

const array = [{ name: "foo1", value: "val1" }, {name: "foo1", value: ["val2", "val3"] }, {name: "foo2", value: "val4"}];
const start = array.reduce((object, {name}) => ({...object, [name]: []}), {});
const result = array.reduce((object, {name, value}) => ({...object, [name]: [object[name], [value]].flat(2)}), start);
const output = Object.entries(result).map(([name, value]) => ({name: name, value: value}));
console.log(output);

Solution 14 - Javascript

A much more easier approach is this 2022:

  var array = [        {            name: "foo1",            value: "val1"        }, {            name: "foo1",            value: [                "val2",                "val3"            ]
        }, {
            name: "foo2",
            value: "val4"
        }
    ];
    
    var output = [        {            name: "foo1",            value: [                "val1",                "val2",                "val3"            ]
        },
        {
            name: "foo2",
            value: [
                "val4"
            ]
        }
    ];
    
    function mergeBasedOnKey(list){
      let c = Object.values(list.reduce((a, b) => {
    
        a[b.name] = a[b.name] || {name: b.name, value: []}
        if(typeof(b['value']) == "string"){
          a[b.name].value.push(b['value'])
        }
        else{
          a[b.name].value = [...a[b.name].value, ...b.value]
        }
    
        return a
    
      }, {}))
      return c
    }
   let ans = mergeBasedOnKey(array)
   console.log(ans)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionzdebruineView Question on Stackoverflow
Solution 1 - JavascriptBenGView Answer on Stackoverflow
Solution 2 - JavascriptHunan RostomyanView Answer on Stackoverflow
Solution 3 - JavascriptpaolobuenoView Answer on Stackoverflow
Solution 4 - JavascriptNguyễn Văn PhongView Answer on Stackoverflow
Solution 5 - JavascripttrincotView Answer on Stackoverflow
Solution 6 - JavascriptRacil HilanView Answer on Stackoverflow
Solution 7 - JavascriptunamagaView Answer on Stackoverflow
Solution 8 - Javascriptqahtan saidView Answer on Stackoverflow
Solution 9 - JavascriptJoe105598055View Answer on Stackoverflow
Solution 10 - Javascriptvigrahalaprasad View Answer on Stackoverflow
Solution 11 - JavascriptBrandon McConnellView Answer on Stackoverflow
Solution 12 - JavascriptLookWorldView Answer on Stackoverflow
Solution 13 - JavascriptGilView Answer on Stackoverflow
Solution 14 - JavascriptoptimusView Answer on Stackoverflow