简体   繁体   中英

How to merge objects with the same properties into a single Array?

I'm reciving the next data

    [
      { id: "1", name: "test1", rName: "the1" },
      { id: "1", name: "test1", rName: "the2" },
      { id: "1", name: "test1", rName: "the3" },
      { id: "2", name: "test2", rName: "the1" },
      { id: "2", name: "test2", rName: "the2" },
      { id: "3", name: "test3", rName: "the1" }
    ]

I want to merge it by the id and push the rName's into an array to have this structure

    [
      { id: "1", name: "test1", rName: ["the1", "the2","the3"] },
      { id: "2", name: "test2", rName: ["the1", "the2"] },
      { id: "3", name: "test3", rName: ["the1"] }
    ]

I thought doing it with reduce but didn't succeed, if anyone can point me to the right direction it will be greatly appreciated.

This is a pretty simple case of data reformatting. Code is below.

 var data = [ { id: "1", name: "test1", rName: "the1" }, { id: "1", name: "test1", rName: "the2" }, { id: "1", name: "test1", rName: "the3" }, { id: "2", name: "test2", rName: "the1" }, { id: "2", name: "test2", rName: "the2" }, { id: "3", name: "test3", rName: "the1" } ]; var reduced = Object.values(data.reduce(function(accumulator, element) { if (!accumulator[element.id]) { accumulator[element.id] = { id: element.id, name: element.name, rName: [] }; } accumulator[element.id].rName.push(element.rName); return accumulator; }, {})); console.log(reduced); 

The accumulator checks if the key by element.id exists in the accumulator. If it does not, it creates it. It then pushes the new rName on the existing stack. Object.values() is then used to make the conversion back to an array.

You can use reduce and find like this:

In the reduce accumulator , check if there is already an item with same id as the current item being iterated. If yes, push the current item's rName to the rName array. Else, push a new item to the accumulator

 var data = [ { id: "1", name: "test1", rName: "the1" }, { id: "1", name: "test1", rName: "the2" }, { id: "1", name: "test1", rName: "the3" }, { id: "2", name: "test2", rName: "the1" }, { id: "2", name: "test2", rName: "the2" }, { id: "3", name: "test3", rName: "the1" } ]; const newArray = data.reduce((acc, {id,name,rName}) => { const existing = acc.find(a => a.id == id); if (existing) existing["rName"].push(rName); else acc.push({id,name,rName: [rName]}) return acc }, []); console.log(newArray) 

This is a one line, code-golf answer. (Got the idea from @Sébastien's answer):

 var data = [ { id: "1", name: "test1", rName: "the1" }, { id: "1", name: "test2", rName: "the2" }, { id: "1", name: "test2", rName: "the3" }, { id: "2", name: "test2", rName: "the1" }, { id: "2", name: "test1", rName: "the2" }, { id: "3", name: "test3", rName: "the1" } ] const anotherArray = Object.values(data.reduce((acc, {id,name,rName}) => ((acc[id] = acc[id] || {id,name,rName:[]})["rName"].push(rName), acc), {})); console.log(anotherArray) 

Pure ES6

let result = obj.reduce((acc, item) => {
  let found = acc.find(i => i.id === item.id);
  found ? (found.rName = [...found.rName, item.rName]) : (acc = [...acc, { ...item, rName: [item.rName] }]);
  return acc;
}, []);

If each id can determine a unique name , this will work:

let data = [
  { id: "1", name: "test1", rName: "the1" },
  { id: "1", name: "test1", rName: "the2" },
  { id: "1", name: "test1", rName: "the3" },
  { id: "2", name: "test2", rName: "the1" },
  { id: "2", name: "test2", rName: "the2" },
  { id: "3", name: "test3", rName: "the1" }
];

let result = [];

for (let item of data) 
{
    const index = result.findIndex(i => i.id === item.id);
    if (index < 0) {
        result.push({ id: item.id, name: item.name, rName: [item.rName] });
    }
    else {
        result[index].rName.push(item.rName);
    }
}

Please note this will not work when following data should accept:

[
  { id: "1", name: "test1", rName: "the1" },
  { id: "1", name: "test2", rName: "the2" },
  { id: "1", name: "test2", rName: "the3" },
  { id: "2", name: "test2", rName: "the1" },
  { id: "2", name: "test1", rName: "the2" },
  { id: "3", name: "test3", rName: "the1" }
]

If you adhere to use method 'reduce', you can do like this:

var arr = [
        { id: "1", name: "test1", rName: "the1" },
        { id: "1", name: "test1", rName: "the2" },
        { id: "1", name: "test1", rName: "the3" },
        { id: "2", name: "test2", rName: "the1" },
        { id: "2", name: "test2", rName: "the2" },
        { id: "3", name: "test3", rName: "the1" }
    ]
    var arr1 = arr.reduce(function (a1, a2) {
        if (a1 instanceof Array) {
            let lastItem = a1[a1.length - 1]
            if (lastItem.id == a2.id) {
                lastItem.rName.push(a2.rName)
            } else {
                a1.push({ ...a2, rName: [a2.rName] })
            }
            return a1
        } else {
            let result = []
            if (a1.id == a2.id) {
                result.push({ ...a1, rName: [a1.rName, a2.rName] })
            } else {
                result.push({ ...a1, rName: [a1, rName] })
                result.push({ ...a2, rName: [a2, rName] })
            }
            return result

        }

    })
    console.log(JSON.stringify(arr1))

But I think you should do like this, for the code is more clear:

var arr = [
    { id: "1", name: "test1", rName: "the1" },
    { id: "1", name: "test1", rName: "the2" },
    { id: "1", name: "test1", rName: "the3" },
    { id: "2", name: "test2", rName: "the1" },
    { id: "2", name: "test2", rName: "the2" },
    { id: "3", name: "test3", rName: "the1" }
]
var map = new Map()
arr.forEach(function(item){
    if(map.has(item.id)){
        map.get(item.id).rName.push(item.rName)
    }else{
        map.set(item.id, {...item, rName: [item.rName]})
    }
})
var arr1 = Array.from(map.values())
console.log(JSON.stringify(arr1))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM