Reputation: 11478
Let's say I've got these two arrays:
const initial: Array<GivenObj> = [
{name: 'a', times: 40, other: 50},
{name: 'b', times: 10, other: 15},
{name: 'c', times: 15, other: 12}
];
const toBeMerged: Array<GivenObj> = [
{name: 'a', times: 45, other: 30},
{name: 'c', times: 10, other: 10},
{name: 'd', times: 23, other: 10}
];
These two arrays contain different values, but similar keys. I need to aggregate this data to a single array that will contain both of their values, but uniquely.
In code, the two arrays above should be aggregated as follows:
const aggregated: Array<GivenObj> = [
{name: 'a', times: 85, other: 80},
{name: 'b', times: 10, other: 15},
{name: 'c', times: 25, other: 22},
{name: 'd', times: 23, other: 10}
];
I was wondering what's the best way to aggregate data between two arrays.
Upvotes: 3
Views: 874
Reputation: 386654
You could reduce the given data and look for same name
, then update, otherwise add a new object.
It takes
initial
and toBeMerged
,reducing the items by
name
in the accumulator r
,const
initial = [{ name: 'a', times: 40, other: 50 }, { name: 'b', times: 10, other: 15 }, { name: 'c', times: 15, other: 12 }],
toBeMerged = [{ name: 'a', times: 45, other: 30 }, { name: 'c', times: 10, other: 10 }, { name: 'd', times: 23, other: 10 }],
merged = [...initial, ...toBeMerged].reduce((r, o) => {
var temp = r.find(p => o.name === p.name);
if (!temp) return [...r, { ...o }];
temp.times += o.times;
temp.other += o.other;
return r;
}, []);
console.log(merged);
Upvotes: 1
Reputation: 17190
Here you have one approach using reduce() and findIndex() over the new array to be merged. If a new object to be merged already exists (i.e the name
property match for some object), we increment the rest of the matching properties and add the non-existing ones, otherwise we push the entire new object:
const initial = [
{name: 'a', times: 40, other: 50},
{name: 'b', times: 10, other: 15},
{name: 'c', times: 15, other: 12}
];
const toBeMerged = [
{name: 'a', times: 45, other: 30, another: 76},
{name: 'c', times: 10, other: 10},
{name: 'd', times: 23, other: 10}
];
let newArray = toBeMerged.reduce((res, curr) =>
{
let found = res.findIndex(x => x.name === curr.name);
if (found >= 0)
{
res[found] = Object.keys(curr).reduce((r, c) =>
{
r[[c]] = (r[[c]] && c !== 'name') ? r[[c]] + curr[[c]] : curr[[c]];
return r;
}, res[found]);
}
else
{
res.push(curr);
}
return res;
}, initial);
console.log(newArray);
Upvotes: 0
Reputation: 16576
Here is how I have approached this problem in the past.
final
array, which will contain the final result.concat
methodname
property, meaning that all the a's will be in order, then the b's, etc. forEach
method is used to iterate through the concatenated, sorted array. If the current element has the same name
property as the last element of the final
array, then add the numeric properties of el
to the last element of the final
array. Otherwise, add el
to the end of the final array.const initial = [
{name: 'a', times: 40, other: 50},
{name: 'b', times: 10, other: 15},
{name: 'c', times: 15, other: 12}
];
const toBeMerged = [
{name: 'a', times: 45, other: 30},
{name: 'c', times: 10, other: 10},
{name: 'd', times: 23, other: 10}
];
let final = [];
initial.concat(toBeMerged).sort((a, b) => a.name > b.name).forEach(el => {
if (final.length > 0 && el.name === final[final.length - 1].name) {
final[final.length - 1].times += el.times;
final[final.length - 1].other += el.other;
} else {
final.push(el);
}
})
console.log(final);
Upvotes: 0
Reputation: 37755
You can do it with help of reduce.
concat
.reduce
we check if the object property is already present in output
than we add times
and other
in existing property if not than we add a new property. const initial= [{name: 'a', times: 40, other: 50},{name: 'b', times: 10, other: 15},{name: 'c', times: 15, other: 12}];
const toBeMerged= [{name: 'a', times: 45, other: 30},{name: 'c', times: 10, other: 10},{name: 'd', times: 23, other: 10}];
let temp = initial.concat(toBeMerged)
let op = temp.reduce((output,current)=>{
if( output[current.name] ){
output[current.name].times += current.times
output[current.name].other += current.other
} else{
output[current.name] = current;
}
return output;
},{})
console.log(Object.values(op))
Upvotes: 0
Reputation: 17616
Using spread operator, destructuring, Array#reduce, Object#values and Map
const initial=[{name:'a',times:40,other:50},{name:'b',times:10,other:15},{name:'c',times:15,other:12}];const toBeMerged=[{name:'a',times:45,other:30},{name:'c',times:10,other:10},{name:'d',times:23,other:10}]
const res = [...[...initial, ...toBeMerged]
.reduce((a,{name,times,other})=>{
const b = a.get(name);
return a.set(name,{name, times: (b?b.times:0) + times, other: (b?b.other:0) + other});
}, new Map()).values()];
console.log(res);
Upvotes: 0
Reputation: 3358
I'd approach this by merging the two arrays, then running a reduce against that combined array.
Inside the reduce, it first checks to see if an entry with that name exists, if not it pushes that entry to the result array. If it does find an existing entry, it creates any property that doesn't exist, and adds the values of any numerical properties that already exist. This should be flexible enough for whatever your actual use case is.
const initial = [
{name: 'a', times: 40, other: 50},
{name: 'b', times: 10, other: 15},
{name: 'c', times: 15, other: 12}
];
const toBeMerged = [
{name: 'a', times: 45, other: 30},
{name: 'c', times: 10, other: 10},
{name: 'd', times: 23, other: 10}
];
const result = [ ...initial, ...toBeMerged ].reduce((arr, t) => {
let existing = arr.filter(x => x.name == t.name)[0]
if(!existing) arr.push(t)
else {
const keys = Object.keys(t)
keys.forEach(key => {
if(!existing.hasOwnProperty(key)) existing[key] = t[key]
else if(typeof existing[key] === "number") existing[key] += t[key]
})
}
return arr
}, [])
console.log(result)
Upvotes: 3
Reputation: 4915
Try this code
const initial = [
{name: 'a', times: 40, other: 50},
{name: 'b', times: 10, other: 15},
{name: 'c', times: 15, other: 12}
];
const toBeMerged = [
{name: 'a', times: 45, other: 30},
{name: 'c', times: 10, other: 10},
{name: 'd', times: 23, other: 10}
];
//console.log(initial);
function arrayUnique(array) {
var a = array.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i].name === a[j].name)
{
a[i].times +=a[j].times;
a[i].other +=a[j].other;
a.splice(j--, 1);
}
}
}
return a;
}
// Merges both arrays and gets unique items
var array = arrayUnique(initial.concat(toBeMerged));
console.log(array);
Upvotes: 0