Shei
Shei

Reputation: 483

Merge 2 objects in an array where the values are an array

I am trying to merge values in 2 objects from the same array. The objects in this case are similar and the values I want to merge are arrays(Set)

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

Expected Output

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

My Code

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]);
    let newValue = new Set(output[existingIndex].value).add(item.value)
    output[existingIndex].value = Array.from(newValue);
  } else {
    output.push(item);
  }
});

Output Gotten

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

How can I get the expected output (ES6 would also be preferred)

Upvotes: 1

Views: 112

Answers (8)

Arpan
Arpan

Reputation: 11

Find unique values of keys. Match this keys within array and return unique objects. Push this objects in an empty array. Then match other objects value with the new arrays objects value and push the unmatched values to this new array.

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

let key = [];
arr.map((val)=>key.push(val.name));
let uniquekeys = [...new Set(key)]; //unique values of keys

let newarr = [];
uniquekeys.map((uniquekey,ind)=>{

        let reduceunique = arr.filter((vals)=>uniquekey == vals.name);   // return matching objects as array     
        newarr.push(reduceunique[0]); // Push unique objects in an empty array
        
        for(let i = 1; i<uniquekeys.length;i++){ 
                reduceunique[i].value.map((val)=>{
                        let existvalue = newarr[ind].value.indexOf(val); // Match every value with the unique objects values
                                if(existvalue<0){
                                        newarr[ind].value.push(val); // push the unmatched value in the array
                                }
                });
        };
});
console.log(newarr);

Upvotes: 1

Thomas Sablik
Thomas Sablik

Reputation: 16454

One approach is to create an unique list of keys and iterate over it. Create an array for each key and merge the values. The vanilla js way is:

Array.from(new Set(array.map(el => el.name)))
    .map(name => ({
        name,
        value: Array.from(new Set(array.filter(el => el.name === name).flatMap(el => el.value)))
    }))

Example:

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

console.log(Array.from(new Set(array.map(el => el.name)))
.map(name => ({
    name,
    value: Array.from(new Set(array.filter(el => el.name === name).flatMap(el => el.value)))
})));

Using lodash you can reduce it to

_.uniq(array.map(el => el.name))
    .map(name => ({
        name,
        value: _.uniq(array.filter(el => el.name === name).flatMap(el => el.value))
    }))

Example:

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

console.log(_.uniq(array.map(el => el.name))
    .map(name => ({
        name,
        value: _.uniq(array.filter(el => el.name === name).flatMap(el => el.value))
    })));
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

Upvotes: 1

Nenad Vracar
Nenad Vracar

Reputation: 122037

You could use reduce method with a Map as accumulator value and then use spread syntax ... on Map values to get an array of values.

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

const map = array.reduce((r, { name, value }) => {
  if (!r.has(name)) r.set(name, { name, value })
  else r.get(name).value.push(...value)
  r.get(name).value = [...new Set(r.get(name).value)]
  return r;
}, new Map)

const result = [...map.values()]

console.log(result)

Upvotes: 1

Molham Al Nasr
Molham Al Nasr

Reputation: 99

try to use Array.reduce and Array.filter to get the result like the following

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

res = array.reduce((prev, curr) => {
  let index = prev.findIndex(item => item.name === curr.name);
 
  if(index > -1) {
    
    prev[index].value = [...prev[index].value, ...curr.value];
    prev[index].value = prev[index].value.filter((v,i) => prev[index].value.indexOf(v) === i)
  } else {
    prev.push(curr);
  }
  return prev;
},[]);

console.log(res);

Upvotes: 0

Md Sabbir Alam
Md Sabbir Alam

Reputation: 5054

You can do the following using reduce,

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

res = array.reduce((prev, curr) => {
  let index = prev.findIndex(item => item.name === curr.name);
 
  if(index > -1) {
    s = new Set([...prev[index].value, ...curr.value]);
    prev[index].value = Array.from(s);
  } else {
    prev.push(curr);
  }
  return prev;
},[]);

console.log(res);

Upvotes: 1

Kartik Narang
Kartik Narang

Reputation: 201

try

var arr = [
  {
  name: "foo1",
  value: ["val1","val2"]
}, 
{
  name: "foo1",
  value: ["val2", "val3"]
}, 
{
  name: "foo2",
  value: ["val4"]
},
 {
  name: "foo2",
  value: ["val4","val5"]
},
];
var arr2={}
arr.map((elem,ind)=>{
    if(!arr2[elem.name]){
        arr2[elem.name]=[]
    }
    arr2[elem.name]=[...arr2[elem.name],...elem.value]
})
arr=Object.keys(arr2);
arr.map((elem,ind)=>{
    arr[ind]={name:elem,value:arr2[elem]};
})

Upvotes: 1

gscHeartA
gscHeartA

Reputation: 44

Is this code solve your problem?

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

  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]);
    let newValue = new Set(output[existingIndex].value.concat(item.value))
    output[existingIndex].value = Array.from(newValue);
  } else {
    output.push(item);
  }
});

Upvotes: 1

Naren
Naren

Reputation: 4470

Try this

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

const result = []

for (const item of array) {
  const existingItem = result.find(i => i.name === item.name)
  if (existingItem) {
    existingItem.value = [...new Set([...existingItem.value, ...item.value])]
  } else {
    result.push(item)
  }
}

console.log(result)

Upvotes: 2

Related Questions