Reputation: 779
There are two arrays:
[
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 1},
{"id": "344430b94t4t34rwefewfdff", "quantity": 5},
{"id": "342343343t4t34rwefewfd53", "quantity": 3}
]
and
[
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 2},
{"id": "344430b94t4t34rwefewfdff", "quantity": 1}
]
How to combine them into one summing quantity?
[
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 3},
{"id": "344430b94t4t34rwefewfdff", "quantity": 6},
{"id": "342343343t4t34rwefewfd53", "quantity": 3}
]
One of them can be empty sometimes
Upvotes: 10
Views: 7520
Reputation: 304
This function uses lodash reduce and mapValues to sum the specified keys of an array of objects into a single result object. It allows missing keys in each object.
const mergeAndSumObjVals = (objs, keys) => _.reduce(
objs,
(o, s) => _.mapValues(o, (v, k) => (v || 0) + (s[k] || 0)),
_.chain(keys).invert().mapValues(() => 0).value(),
)
const objs = [{
negative: 54,
positive: 2
}, {
inconclusive: 8,
positive: 1
}, {
negative: 26,
inconclusive: 5,
positive: 4
}]
const result = mergeAndSumObjVals(objs, ['negative', 'inconclusive', 'positive'])
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
Upvotes: 0
Reputation: 4076
All these answers require you to know the object structure to select and sum.
lodash does actually allow you to do this without knowing the structure; by using the customizer
parameter of _.mergeWidth
;
let result = _.mergeWith(arr1, arr2, (objValue, srcValue, key, object, source, stack) =>{
//Add any conditions you need here. Ive added a few relevant examples.
//if(key.startsWith("num")) //Check for property name prefixes like num...
//if(propertyNamesToSum.Includes(key)) //check if your property is in your predefined list of property names
//This one below sums any properties that are numbers
if(_.isNumber(srcValue) && _.isNumber(objValue)){
return srcValue + objValue;
}
return undefined; //lodash will merge as usual if you return undefined.
});
Lodash docs - https://lodash.com/docs/4.17.15#mergeWith
Upvotes: 2
Reputation: 37
You can use .reduce and .find methods to achieve this.
const arr1 = [{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 1}, {"id": "344430b94t4t34rwefewfdff", "quantity": 5}, {"id": "342343343t4t34rwefewfd53", "quantity": 3}];
const arr2 = [{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 2}, {"id": "344430b94t4t34rwefewfdff", "quantity": 1}];
const result = [...arr1, ...arr2].reduce((accumulator, currentValue) => {
const element = accumulator.find(item => item.id === currentValue.id)
element ? element.quantity += currentValue.quantity : accumulator.push(currentValue)
return accumulator
},[])
console.log(result)
Upvotes: 1
Reputation: 1240
Version with additional object keys. The body of the function does not interfere with what object has properties. So sum by "qty" and check by "prop"
var first = [
{quantity:100, id:1, variantId: 1},
{quantity:300, id:2, variantId: 2, propA: 'aaa'},
];
var second = [
{quantity:100, id:1, variantId: 1},
{quantity:200, id:2, variantId: 2, propB: true},
{quantity:300, id:3, variantId: 3, propC: 'ccc'},
]
function mergeArrays(arrayOfArrays, propToCheck, propToSum) {
let sum = [];
[].concat(...arrayOfArrays).map(function(o) {
let existing = sum.filter(function(i) { return i[propToCheck] === o[propToCheck] })[0];
if (!existing) {
sum.push(o);
} else {
existing[propToSum] += o[propToSum];
let copyProps = Object.keys(o).filter(obj => {
return existing[obj] !== o[obj]
}).map(val => (val !== propToSum) ? existing[val] = o[val] : null)
}
});
return sum;
}
console.log(mergeArrays([first, second], 'variantId', 'quantity'))
Upvotes: 0
Reputation: 63524
You can use lodash but modern vanilla JS is just as viable and performant. I would imagine the other answers will be using functional methods like reduce
, so here's a version that uses a simple for/of
loop with find
rather than a dictionary lookup which might be longer, but it is a little easier to follow.
const arr1 = [{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 1}, {"id": "344430b94t4t34rwefewfdff", "quantity": 5}, {"id": "342343343t4t34rwefewfd53", "quantity": 3}];
const arr2 = [{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 2}, {"id": "344430b94t4t34rwefewfdff", "quantity": 1}];
function merge(arr1, arr2) {
// Merge the arrays, and set up an output array.
const merged = [...arr1, ...arr2];
const out = [];
// Loop over the merged array
for (let obj of merged) {
// Destructure the object in the current iteration to get
// its id and quantity values
const { id, quantity } = obj;
// Find the object in out that has the same id
const found = out.find(obj => obj.id === id);
// If an object *is* found add this object's quantity to it...
if (found) {
found.quantity += quantity;
// ...otherwise push a copy of the object to out
} else {
out.push({ ...obj });
}
}
return out;
}
console.log(merge(arr1, arr2));
Upvotes: 4
Reputation: 13963
You can do it with plain JavaScript.
Use Array.reduce()
to make an intermediate dictionary by id and accumulate the quantities, then turn it into an array with Object.values()
:
const arr1 = [
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 1},
{"id": "344430b94t4t34rwefewfdff", "quantity": 5},
{"id": "342343343t4t34rwefewfd53", "quantity": 3}
];
const arr2 = [
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 2},
{"id": "344430b94t4t34rwefewfdff", "quantity": 1}
];
const result = Object.values([...arr1, ...arr2].reduce((acc, { id, quantity }) => {
acc[id] = { id, quantity: (acc[id] ? acc[id].quantity : 0) + quantity };
return acc;
}, {}));
console.log(result);
Upvotes: 13
Reputation: 30685
You can just do this with reduce:
let a1 = [
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 2},
{"id": "344430b94t4t34rwefewfdff", "quantity": 1}
];
let a2 = [
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 1},
{"id": "344430b94t4t34rwefewfdff", "quantity": 5},
{"id": "342343343t4t34rwefewfd53", "quantity": 3}
];
let result = Object.values(a1.concat(a2).reduce((acc, v) => {
if (!acc[v.id]) {
acc[v.id] = {id: v.id, quantity: 0};
}
acc[v.id].quantity += v.quantity;
return acc;
}, {}));
console.log("Results: ", result);
Upvotes: 2