Reputation: 199
Im facing a tricky problem in forming a JSON array.
I have a below JSON where products are duplicated,
var mainJson = [{
"product": "pen",
"quantity": 3
}, {
"product": "pen",
"quantity": 3
}, {
"product": "pencil",
"quantity": 4
}, {
"product": "pencil",
"quantity": 4
}]
now i want to remove the duplicated and i want to add Quantity field and my final output should look like below...
var finalOutput = [{
"product":"pen",
"quantity":6
},{
"product":"pencil",
"quantity":8
}]
Im able to remove the duplicate records with same product name but im not able to concatenate the quantity field at the time of elimination..
Can someone please help me to resolve this?
Thank you in advance
Upvotes: 1
Views: 1204
Reputation: 15530
You may walk through your source array, using Array.prototype.reduce()
and insert into resulting array item, having product
value found for the first time, or add current quantity
should one already exist:
const mainJson = [{"product":"pen","quantity":3},{"product":"pen","quantity":3},{"product":"pencil","quantity":4},{"product":"pencil","quantity":4}],
groupped = mainJson.reduce((res,{product,quantity}) => {
const group = res.find(item => item.product == product)
group ?
group.quantity += quantity :
res.push({product,quantity})
return res
}, [])
console.log(groupped)
.as-console-wrapper {min-height: 100%}
EDIT:
Above algorithm (having O(n²) time complexity) will perform nicely when the number of unique product items is relatively small, however for large number of product items, it is reasonable (just like @CameronDowner and @StepUp suggest) to build up a sort of hash map (first pass) with products and respective totals and transform that into array of desired format (second pass) which makes it O(n) time complexity:
const mainJson = [{"product":"pen","quantity":3},{"product":"pen","quantity":3},{"product":"pencil","quantity":4},{"product":"pencil","quantity":4}],
groupObj = mainJson.reduce((r,{product,quantity}) =>
(r[product] = (r[product]||0) + quantity, r), {}),
group = Object.keys(groupObj).map(key => ({product:key, quantity: groupObj[key]}))
console.log(group)
.as-console-wrapper {min-height: 100%}
Upvotes: 2
Reputation: 38134
We can use reduce
function:
const result = mainJson.reduce( (a, {product, quantity})=> {
a[product] = a[product] || {product, quantity: 0};
a[product].quantity += quantity;
return a;
},{})
An example:
var mainJson = [{
"product": "pen",
"quantity": 3
}, {
"product": "pen",
"quantity": 3
}, {
"product": "pencil",
"quantity": 4
}, {
"product": "pencil",
"quantity": 4
}];
const result = mainJson.reduce( (a, {product, quantity})=> {
a[product] = a[product] || {product, quantity: 0};
a[product].quantity += quantity;
return a;
},{})
console.log(Object.values(result));
If you are bothering about performance, then you can use the following solution.
const result = mainJson.reduce( (a, {product, quantity})=> {
a[product] = a[product] || {product, quantity: 0};
a[product].quantity += quantity;
return a;
},{})
let vals = [];
for (var key in result) {
if (result.hasOwnProperty(key) ) {
vals.push(result[key]);
}
}
console.log(vals);
You can see results at JSBench.me
Upvotes: 1
Reputation: 647
While the reduce
method is quite Javascript, if you prefer a more generic way, you can do it in two steps:
Map
with no duplicated items;Map
and get your final JSON.Take in mind this solution is the hard way, and it is slower than the reduce
method. I will not blame you if you keep with the nicer reduce
option, I just wanted to point out this more generic way! The result is the same.
Following the steps, we have:
var mainJson = [{"product": "pen","quantity": 3}, {"product": "pen","quantity": 3},
{"product": "pencil","quantity": 4}, {"product": "pencil","quantity": 4}];
// Step 1
var mapJson = new Map();
for(let item of mainJson)
{
if (mapJson.has(item.product))
mapJson.set(item.product, mapJson.get(item.product) + item.quantity);
else
mapJson.set(item.product, item.quantity);
}
// Step 2
var finalJson = [];
for(let item of mapJson)
finalJson.push({product:item[0], quantity:item[1]});
console.log(finalJson);
Upvotes: 1
Reputation: 2038
I would do this in two stages. First I would reduce the array into an object, with the product
as the key and the quantity
as the value.
This handles the duplicates very well because object keys can never be duplicated.
I would then map this, using Object.entries, back to an array in the desired format. Using array destructuring can make this step very clean.
const mainJson = [
{
product: "pen",
quantity: 3
},
{
product: "pen",
quantity: 3
},
{
product: "pencil",
quantity: 4
},
{
product: "pencil",
quantity: 4
}
];
const productQuantities = mainJson.reduce((acc, curr) => {
const { product, quantity } = curr;
const currentValue = acc[product] || 0; // default to zero if not set yet
return {
...acc,
[product]: currentValue + quantity
};
}, {});
console.log(productQuantities);
const productQuantitiesArray = Object.entries(
productQuantities
).map(([product, quantity]) => ({ product, quantity }));
console.log(productQuantitiesArray);
Upvotes: 2