Hi,
I have one array of objects with all items and their respective price like this:
var items = [
{
"id": "001",
"name": "apple",
"price": 500
},
{
"id": "002",
"name": "banana",
"price": 700
},
{
"id": "003",
"name": "pear",
"price": 200
}
];
then I have a client's car like this:
var cart = [{
"id": "001",
"qty": 2
},
{
"id": "002",
"qty": 3
},
{
"id": "003",
"qty": 4
}
];
client's credit is stored in a variable. I want to check the second array against the first one to get the total of the cart and make sure it wont exceed client's credit. Im not sure how to do it though. I tried:
var mytotal=cart.map(d => {
var total=0;
items.forEach(rm => {
total = total+(d.qty*rm.price);
} return total;
});
if(credit >= total) {//dosomething}
but it didnt work. What is the right approach?
Thank you.
You can divide your problem into two tasks: join and sum.
Join
const joined = items.map(item => ({...item, ...cart.find(c => c.id === item.id)}));
Note that in case the id won't match, find will return null, and the spread (...) will result in no change to the object
Sum
const sum = joined.reduce((sum, curr) => sum += curr.price * curr.qty, 0);
A safer version would be:
const sum = joined.reduce((sum, curr) => sum += (curr.price ?? 0) * (curr.qty ?? 0), 0);
var items = [
{
"id": "001",
"name": "apple",
"price": 500
},
{
"id": "002",
"name": "banana",
"price": 700
},
{
"id": "003",
"name": "pear",
"price": 200
}
];
var cart = [{
"id": "001",
"qty": 2
},
{
"id": "002",
"qty": 3
},
{
"id": "003",
"qty": 4
}
];
const joined = items.map(item => ({...item, ...cart.find(c => c.id === item.id)}));
const sum = joined.reduce((sum, curr) => sum += curr.price * curr.qty, 0);
console.log(`joined object is: `, joined);
console.log(`sum is: ${sum}`);
I am a little late in the game, but maybe the following snippet is still of interest? It picks up the idea of creating a lookup object itms and for each shopping cart entry it also combines two objects into a new one with a subtotal subt, so you can easliy create a meaningfull shopping cart table. The variable ttl is updated alongside and contains the total sum:
const items = [
{
"id": "001",
"name": "apple",
"price": 500
},
{
"id": "002",
"name": "banana",
"price": 700
},
{
"id": "003",
"name": "pear",
"price": 200
}
],
cart = [{
"id": "001",
"qty": 2
},
{
"id": "002",
"qty": 3
},
{
"id": "003",
"qty": 4
}
];
// Turn the items array into an object, facilitating a fast lookup:
const itms=items.reduce((a,c)=>(a[c.id]=c,a),{});
let ttl=0;
// calculate the totals:
const res=cart.map(c=>{
const p=itms[c.id], subt=c.qty*p.price;
ttl+=subt;
return {...c,...p,subt}
})
// Show the result:
console.log(res,ttl);
To implement a reusable and efficient solution you can create a lookup table on the items array, here using a Map, which allows you to directly access the item by id.
const itemLookup = new Map(items.map((item) => [item.id, item]))
// Map(3) {
// '001' => { id: '001', name: 'apple', price: 500 },
// '002' => { id: '002', name: 'banana', price: 700 },
// '003' => { id: '003', name: 'pear', price: 200 }
// }
You can then create a getCartTotal helper which will use the lookup table to total the cart passed to it. (Here we are assuming that any item that will be in the cart will also be in the items array, but for safety you could add optional chaining, t += (itemLookup.get(id)?.price ?? 0) * qty)
const getCartTotal = (cart) => {
return cart.reduce((t, { id, qty }) => (
t += itemLookup.get(id).price * qty
), 0);
}
The result allows you to efficiently re-sum the cart whenever it changes.
const items = [{ "id": "001", "name": "apple", "price": 500 }, { "id": "002", "name": "banana", "price": 700 }, { "id": "003", "name": "pear", "price": 200 }];
const itemLookup = new Map(items.map(({ id, ...item }) => [id, { id, ...item }]));
const getCartTotal = (cart) => {
return cart.reduce((total, { id, qty }) => (
total += itemLookup.get(id).price * qty
), 0);
}
const cart = [{ "id": "001", "qty": 2 }, { "id": "002", "qty": 3 }, { "id": "003", "qty": 4 }];
console.log(getCartTotal(cart)); // 3900
cart[0].qty += 2;
console.log(getCartTotal(cart)); // 4900
In your attempt, you're applying the map function to the cart array. Map applies the function to each element of the array, and returns the new array. The function total = 0 etc is being applied for each element separately.
In the map/reduce way of working, try getting the proper price of each cart element first with map, and then summarize using reduce.
Map/reduce is just one of many ways to solve this issue, you could also do it iteratively with a for loop like you were trying within your map function.
Related
I am trying to create an object that will be filled with values from an existing object. In my case it should have as result per "Material" an object list with the "Code" and the "Quantity". In addition, if the "Material" and "Code" are the same, the "Quantity" should be summed up.
I hope that you can see from my example what I mean. Thanks
const arr = [{
"Material": "123",
"Code": "AAA",
"Quantity": 1
}, {
"Material": "123",
"Code": "BBB",
"Quantity": 2
}, {
"Material": "123",
"Code": "BBB",
"Quantity": 2
}, {
"Material": "456",
"Code": "CCC",
"Quantity": 7
}]
var arrResult = [{
"Material": "123",
"CodeQuantity": [{
"Code": "AAA",
"Quantity": 1
}, {
"Code": "BBB",
"Quantity": 4
}]
}, {
"Material": "456",
"CodeQuantity": [{
"Code": "CCC",
"Quantity": 7
}]
}]
console.log(arr)
console.log("Result:", arrResult)
An approach should consider breaking the OP's entire task into two separate ones.
Within an intermediate step one firstly does reduce the list of material items into a map of (grouped) material items.
The final step does a map-reduce on the values of the intermediate result, where the reduce task is responsible for summing up a material's Code specific Quantity value ...
function collectTotalCodeQuantity(index, item) {
const { Code, Quantity } = item;
// access or create the `Code` specific code quantity item.
const codeQuantityItem = (index[Code] ??= { Code, Quantity: 0 });
// sum up a `Code` specific code quantity item's quantity value.
codeQuantityItem.Quantity += Quantity;
return index;
}
function createMaterialGroup(index, item) {
const { Material: materialValue, ...rest } = item;
// create the `CodeQuantity` key in a more generic way from the
// `rest` object which is ... `item` data without `Material` property.
const compoundKey = Object.keys(rest).join('');
// access or create the `Material` specific group.
const group = index[materialValue] ??= { Material: materialValue };
// access or create the `CodeQuantity` specific list.
const list = group[compoundKey] ??= [];
// push the `rest` object into the `CodeQuantity` specific list.
list.push(rest);
return index;
}
const materialDataItemList = [{
"Material": "123",
"Code": "AAA",
"Quantity": 1
}, {
"Material": "123",
"Code": "BBB",
"Quantity": 2
}, {
"Material": "123",
"Code": "BBB",
"Quantity": 2
}, {
"Material": "456",
"Code": "AAA",
"Quantity": 7
}];
const totalCodeQuantityItemList = Object
.values(
materialDataItemList.reduce(createMaterialGroup, {})
)
.map(materialGroup => {
materialGroup.CodeQuantity = Object.values(
materialGroup.CodeQuantity.reduce(collectTotalCodeQuantity, {})
);
return materialGroup;
});
console.log({ totalCodeQuantityItemList });
console.log(
'... intermediate process result ... map of (grouped) material items ...',
materialDataItemList.reduce(createMaterialGroup, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Edit
Moin! how can I add a new key "CodeQuantityGesamt" where the values of all "Quantity" of a "Material" is summed? example:
{
"Material": "123",
"CodeQuantityGesamt": 5,
"CodeQuantity": [{
"Code": "AAA",
"Quantity": 1
}, {
"Code": "BBB",
"Quantity": 4
}]
}
– InFlames82
This can be achieved by changing the mapping task from ...
.map(materialGroup => {
materialGroup.CodeQuantity = Object.values(
materialGroup.CodeQuantity.reduce(collectTotalCodeQuantity, {})
);
return materialGroup;
});
... to e.g. ...
.map(materialGroup => Object.assign(
materialGroup,
materialGroup.CodeQuantity.reduce(collectCodeQuantityTotals, {})
));
... together with adapting the implementation and changing the name of the former collectTotalCodeQuantity reducer function which becomes collectCodeQuantityTotals.
function collectCodeQuantityTotals(quantities, item, idx, arr) {
const { Code, Quantity } = item;
let {
CodeQuantityTotal = 0,
CodeQuantity = [],
index = {},
} = quantities;
// access ...
let codeQuantityItem = index[Code];
// ... or create the `Code` specific code quantity item.
if (!codeQuantityItem) {
codeQuantityItem = index[Code] = { Code, Quantity: 0 };
// push a newly created item into the CodeQuantity list.
CodeQuantity.push(codeQuantityItem);
}
// sum up a `Code` specific code quantity item's quantity value.
codeQuantityItem.Quantity += Quantity;
// sum up the total (or overall) code quantity value.
CodeQuantityTotal += Quantity;
// return a full collector/accumulator object as long as
// the reduce task takes place because `index` is needed
// as lookup, but return, as the final result, an object
// without the `index` property.
return (idx < arr.length - 1)
&& { CodeQuantityTotal, CodeQuantity, index }
|| { CodeQuantityTotal, CodeQuantity };
}
function createMaterialGroup(index, item) {
const { Material: materialValue, ...rest } = item;
// create the `CodeQuantity` key in a more generic way from the
// `rest` object which is ... `item` data without `Material` property.
const compoundKey = Object.keys(rest).join('');
// access or create the `Material` specific group.
const group = index[materialValue] ??= { Material: materialValue };
// access or create the `CodeQuantity` specific list.
const list = group[compoundKey] ??= [];
// push the `rest` object into the `CodeQuantity` specific list.
list.push(rest);
return index;
}
const materialDataItemList = [{
"Material": "123",
"Code": "AAA",
"Quantity": 1
}, {
"Material": "123",
"Code": "BBB",
"Quantity": 2
}, {
"Material": "123",
"Code": "BBB",
"Quantity": 2
}, {
"Material": "456",
"Code": "AAA",
"Quantity": 7
}];
const totalCodeQuantityItemList = Object
.values(
materialDataItemList.reduce(createMaterialGroup, {})
)
.map(materialGroup => Object.assign(
materialGroup,
materialGroup.CodeQuantity.reduce(collectCodeQuantityTotals, {})
));
console.log({ totalCodeQuantityItemList });
console.log(
'... intermediate process result ... map of (grouped) material items ...',
materialDataItemList.reduce(createMaterialGroup, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Updated the solution to match your expectation.
This this. Hope it solves your use case.
const arr = [{
"Material": "123",
"Code": "AAA",
"Quantity": 1
}, {
"Material": "123",
"Code": "BBB",
"Quantity": 2
}, {
"Material": "123",
"Code": "BBB",
"Quantity": 2
}, {
"Material": "456",
"Code": "CCC",
"Quantity": 7
}]
var arrResultExpected = [{
"Material": "123",
"CodeQuantity": [{
"Code": "AAA",
"Quantity": 1
}, {
"Code": "BBB",
"Quantity": 4
}]
}, {
"Material": "456",
"CodeQuantity": [{
"Code": "CCC",
"Quantity": 7
}]
}]
//console.log(arr)
//console.log("ExpecredResult:", arrResultExpected)
const materialIds = arr.map(a => a.Material);
//console.log(materialIds);
const dedupedMaterialIds = [...new Set(materialIds)]
//console.log(dedupedMaterialIds);
const needArr = dedupedMaterialIds.map(entry => {
const matchingEntries = arr.filter(arrEntry => entry === arrEntry.Material);
//console.log(matchingEntries);
var summedResult = [];
matchingEntries.reduce(function(res, value) {
if (!res[value.Code]) {
res[value.Code] = {
Quantity: 0,
Code: value.Code
};
summedResult.push(res[value.Code])
}
res[value.Code].Quantity += value.Quantity
return res;
}, {});
//console.log(summedResult);
return {
Material: entry,
CodeQuantity: [...summedResult]
};
});
console.log(needArr);
try this
const arr = [{
"Material": "123",
"Code": "AAA",
"Quantity": 1
}, {
"Material": "123",
"Code": "BBB",
"Quantity": 2
}, {
"Material": "123",
"Code": "BBB",
"Quantity": 2
}, {
"Material": "456",
"Code": "CCC",
"Quantity": 7
}];
let group = arr.reduce((r, a) => {
r[a.Material] = [...r[a.Material] || [], a];
return r;
}, {});
var newArr = Object.keys(group).map(key => {
var elem = group[key];
var b = elem.map(e => {
return {
Code: e.Code,
Quantity: e.Quantity
}
});
var filteredArr = [];
b.map(i => {
var item = filteredArr.find(n => n.Code == i.Code);
if (item) {
item.Quantity += i.Quantity;
} else {
filteredArr.push(i);
}
})
return {
Material: key,
CodeQuantity: filteredArr
}
});
console.log(newArr);
I would need to check if the objects in the "food" array are equal to each other, and the ones that are - combine into one adding amount and mody.amount and leaving the rest unchanged. This is just for better order data displaying. I tried with lodash library and reduce but but I don't know exactly how to construct this function that nested objects(mody) adds values ​​as well, and in case isEqual returns false keep the object while concurrently concatenating the identical ones.
What I tried:
obj1.reduce((prev, next) => _.isEqual(prev, next) ? {...prev, amount: prev.amount + next.amount} : next
Now it looks like that:
const food = [
{
"id": 1,
"name": "chicken",
"price": 6,
"amount": 1,
"mody": [
{
"id": 33,
"name": "cheese",
"price": 1,
"amount": 1
},
{
"id": 34,
"name": "chips",
"price": 2,
"amount": 1
}
]
},
{
"id": 1,
"name": "chicken",
"price": 6,
"amount": 1,
"mody": [
{
"id": 33,
"name": "cheese",
"price": 1,
"amount": 1
},
{
"id": 34,
"name": "chips",
"price": 2,
"amount": 1
}
]
},
{
"id": 2,
"name": "pizza",
"price": 6,
"amount": 2,
"mody": [
{
"id": 12,
"name": "extra cheese",
"price": 2,
"amount": 1
}
]
}
]
and would need something like that:
const food = [
{
"id": 1,
"name": "chicken",
"price": 6,
"amount": 2,
"mody": [
{
"id": 33,
"name": "cheese",
"price": 1,
"amount": 2
},
{
"id": 34,
"name": "chips",
"price": 2,
"amount": 2
}
]
},
{
"id": 2,
"name": "pizza",
"price": 6,
"amount": 2,
"mody": [
{
"id": 12,
"name": "extra cheese",
"price": 2,
"amount": 1
}
]
}
]
The algorithm to sum the food amounts and mody amounts are almost the same, the difference is that for each food that has the same id we will sum the mody amount. To make the algorithm simpler I used a dictionary as the accumulator on the reduce function so we have a unique element per key. This element will be our final food or mody with the amount sum.
Mody sum algoritm:
const sumMody = (modyAccumulator, currentMody) => {
//Get the stored mody in the accumulator dictionary or null if it the mody is not stored
const storedMody = modyAccumulator[currentMody.id] ?? null
// if mody is null then add mody to the dictionary using its id as key
if (!storedMody) {
modyAccumulator[currentMody.id] = currentMody
} else {
//Mody is stored then sum amount
storedMody.amount += currentMody.amount
}
return modyAccumulator
}
The food sum algoritm is the same as the sumMody, the only difference is that it calls the sumMody function when the foods are equal:
const sumFood = (foodAccumulator, currentFood) => {
//Get the stored foodin the accumulator dictionary or null if it the food is not stored
const storedFood = foodAccumulator[currentFood.id] ?? null
// if food is null then add food to the dictionary using its id as key
if (!storedFood) {
foodAccumulator[currentFood.id] = currentFood
} else {
//Food is stored then sum food amount
storedFood.amount += currentFood.amount
//Create a list with mody from both foods
const modyList = [...storedFood.mody, ...currentFood.mody]
//Use reduce passing the sumMody callback function and initialize the accumulator with a dictionary
const modySumDictionary = modyList.reduce(sumMody, {})
/* The function above return a dictionary where the identifier is the mody.id
and the value is the mody. We only need the values from that dictionary so
we use Object.values to extract all the values. */
storedFood.mody = Object.values(modySumDictionary)
}
return foodAccumulator
}
To execute both sums:
//As explained before the reduce function will return a dictionary so we use Object.values to get only the values
const result = Object.values(food.reduce(sumFood, {}))
console.log(result)
Algoritm without comments:
const sumMody = (modyAccumulator, currentMody) => {
const storedMody = modyAccumulator[currentMody.id] ?? null
if (!storedMody) {
modyAccumulator[currentMody.id] = currentMody
} else {
storedMody.amount += currentMody.amount
}
return modyAccumulator
}
const sumFood = (foodAccumulator, currentFood) => {
const storedFood = foodAccumulator[currentFood.id] ?? null
if (!storedFood) {
foodAccumulator[currentFood.id] = currentFood
} else {
storedFood.amount += currentFood.amount
const modyList = [...storedFood.mody, ...currentFood.mody]
const modySumDictionary = modyList.reduce(sumMody, {})
storedFood.mody = Object.values(modySumDictionary)
}
return foodAccumulator
}
const result = Object.values(food.reduce(sumFood, {}))
console.log(result)
Reference to Object.values
I have an array like this:
[
{
"costs": [{
"value": "80"
}],
"id": 4,
"name": "Subscription Fee",
"month": "March"
},
[
{
"costs": [{
"value": "200"
}],
"id": 2,
"name": "Tution",
"month": "March"
},
{
"costs": [{
"value": "10"
}],
"id": 11,
"name": "DEMO"
}
]
]
I need to have sumation of all the values from costs. How can i do that?
const data = [
{"costs":[{"value":"80"}],"id":4,"name":"Subscription Fee","month":"March"},
[
{"costs":[{"value":"200"}],"id":2,"name":"Tution","month":"March"},
{"costs":[{"value":"10"}],"id":11,"name":"DEMO"}
]
];
// flatten the arrays to get a list of objects
// iterate over this list
const res = data.flat().reduce((total, { costs = [] }) => {
// add the values of this item's costs with total
costs.forEach(({ value = 0 }) => total += +value);
return total;
}, 0);
console.log(res);
I am having an array like,
var result = [
{
"sItem" : [
"Pizza Margherita","Pizza marinara"
],
"sImage" : [
"https://assets.marthastewart.com/styles/wmax-300/d31/pizza-margherita-0606-mla102155/pizza-margherita-0606-mla102155_vert.jpg","https://www.silviocicchi.com/pizzachef/wp-content/uploads/2015/02/m-evid-672x372.jpg"
],
"nQuantity" : 1,
"eDeliveryStatus" : "n",
"nPrice" : 215,
"sReceiptId" : "pRjZpGzIDPpX",
}
];
wants to make Object like, I am running a loop through title array pushing data.
[
{
"title":"Pizza Margherita",
"subtitle":"Pizza Margherita",
"quantity":1,
"price":215,
"currency":"INR",
"image_url":"https://images.mcdelivery.co.in/hardcastle-restaurants-pvt-ltd/image/upload/q_auto:low,fl_lossy,w_300/v1484907263/Items/2754.png"
},
{
"title":"Pizza marinara",
"subtitle":"Pizza marinara",
"quantity":1,
"price":215,
"currency":"INR",
"image_url":"https://www.silviocicchi.com/pizzachef/wp-content/uploads/2015/02/m-evid-672x372.jpg"
}
]
and this is how i am trying but failing :(,
result.forEach(el => {
el.sItem.forEach(el2 => {
elementRec.push({
"title": el2,
"subtitle": el2,
"quantity": el.nQuantity,
"price": el.nPrice,
"currency": "INR",
"image_url": el.sImage
})
});
})
I know this is wrong but new to Javascript.
You are almost there.
In you forEach, add an index parameter and use it to retrieve the right image from the sImage array :
el.sItem.forEach((el2, index) => {
elementRec.push({
"title": el2,
"subtitle": el2,
"quantity": el.nQuantity,
"price": el.nPrice,
"currency": "INR",
"image_url": el.sImage[index]
})
});
var result = [
{
"sItem" : [
"Pizza Margherita",
"Pizza marinara"
],
"sImage" : [
"https://assets.marthastewart.com/styles/wmax-300/d31/pizza-margherita-0606-mla102155/pizza-margherita-0606-mla102155_vert.jpg",
"https://www.silviocicchi.com/pizzachef/wp-content/uploads/2015/02/m-evid-672x372.jpg"
],
"nQuantity" : 1,
"eDeliveryStatus" : "n",
"nPrice" : 215,
"sReceiptId" : "pRjZpGzIDPpX",
}
];
var elementRec = [];
result.forEach(el => {
el.sItem.forEach((el2, index) => {
elementRec.push({
"title": el2,
"subtitle": el2,
"quantity": el.nQuantity,
"price": el.nPrice,
"currency": "INR",
"image_url": el.sImage[index]
})
});
});
console.log(elementRec);
You could map the inner sItem and corresponding sImage to new object by some destruction and short properties.
var data = [{ sItem: ["Pizza Margherita", "Pizza marinara"], sImage: ["https://assets.marthastewart.com/styles/wmax-300/d31/pizza-margherita-0606-mla102155/pizza-margherita-0606-mla102155_vert.jpg", "https://www.silviocicchi.com/pizzachef/wp-content/uploads/2015/02/m-evid-672x372.jpg"], nQuantity: 1, eDeliveryStatus: "n", nPrice: 215, sReceiptId: "pRjZpGzIDPpX" }],
result = data.reduce((r, { sItem, sImage, nQuantity: quantity, nPrice: price }) =>
r.concat(sItem.map((title, i) => ({
title, subTitle: title, quantity, price, currency: 'INR', image_url: sImage[i]
}))),
[]
);
console.log(result);
I think you are trying to do something like this. First create a new array. I'll call it converted. Then push the result objects into it with .forEach() like this.
var converted = [];
result.forEach(function(i){
converted.push({
"title": i.sItem[0],
"subtitle": i.sItem[0],
"quantity": i.nQuantity,
"price": i.nPrice,
"currency": "INR",
"image_url": i.sImage[0]
});
})
Try this fiddle
I am new to JavaScript and was reading about High order functions like map, filter, and reduce.
For hands on I tried following example:
Assume there are some items in shopping cart, use reduce to calculate total cost of items.
var products = [{
"id": 100,
"name": "Dell laptop",
"category": "Laptop",
"price": 40000
}, {
"id": 100,
"name": "LG Mobile",
"category": "Mobile",
"price": 20000
}, {
"id": 100,
"name": "HP laptop",
"category": "Laptop",
"price": 60000
}, {
"id": 100,
"name": "Samsung Mobile",
"category": "Mobile",
"price": 25000
}];
var total = 0;
var result = products.reduce(function (total, product) {
return total + parseInt(product.price);
});
console.log("Total cost of cart : " + result);
Output of the above code was something like below:
Total cost of cart : [object Object]200006000025000
Now I modified the above code and added map as well them it works fine:
var result = products.map(function (product) {
return product.price;
}).reduce(function (total, price) {
return total + price;
});
I get correct result.
Now my question is why I am not able to use the reduce API alone?
Yes, you can. There is no need of two reduce calls. Just pass the initial value as 0 as last parameter to the reduce.
var total = products.reduce(function(prev, curr) {
return prev + curr.price;
}, 0);
// ^^ Initial value
var products = [{
"id": 100,
"name": "Dell laptop",
"category": "Laptop",
"price": 40000
}, {
"id": 100,
"name": "LG Mobile",
"category": "Mobile",
"price": 20000
}, {
"id": 100,
"name": "HP laptop",
"category": "Laptop",
"price": 60000
}, {
"id": 100,
"name": "Samsung Mobile",
"category": "Mobile",
"price": 25000
}];
var total = products.reduce(function(prev, curr) {
return prev + curr.price;
}, 0);
document.body.innerHTML = total;
You'll need to read the documentation:
arr.reduce(callback[, initialValue])
...
The first time the callback is called, previousValue and currentValue can be one of two values. If initialValue is provided in the call to reduce, then previousValue will be equal to initialValue and currentValue will be equal to the first value in the array. If no initialValue was provided, then previousValue will be equal to the first value in the array and currentValue will be equal to the second.
Since you are not providing an initial value, your very first operation is actually {id: ...} + 20000. You need to provide an initialValue of 0 to fix that behaviour.