Grouping and summing array objects - javascript

I'm having a data sample like this
this.userData = [
{id:1, category: 'Food', amount: 30, pDate: '2021-01-13', description: 'test desc'},
{id:2, category: 'Fuel', amount: 10, pDate: '2021-01-12', description: 'test desc'},
{id:3, category: 'Food', amount: 70, pDate: '2021-01-14', description: 'test desc'},
]
What I want to achieve with this data is to group it and sum it up so it comes out like this
[
{name: Food, total: 100},
{name: Fuel, total: 30}
]
What the current code I have, I do not get the output as I want.
const data = this.userData;
const groups = data.reduce((groups, item) => ({
...groups,
[item.category]: [...(groups[item.category] || []), item]
}), {});
console.log(groups);

You could take an object for grouping and get the values.
const
userData = [{ id:1, category: 'Food', amount: 30, pDate: '2021-01-13', description: 'test desc' }, { id:2, category: 'Fuel', amount: 10, pDate: '2021-01-12', description: 'test desc' }, { id:3, category: 'Food', amount: 70, pDate: '2021-01-14', description: 'test desc' }],
groups = Object.values(userData.reduce((r, o) => {
(r[o.category] ??= { name: o.category, total: 0 }).total += o.amount;
return r;
}, {}))
console.log(groups);

Try this
const userData = [
{id:1, category: 'Food', amount: 30, pDate: '2021-01-13', description: 'test desc'},
{id:2, category: 'Fuel', amount: 10, pDate: '2021-01-12', description: 'test desc'},
{id:3, category: 'Food', amount: 70, pDate: '2021-01-14', description: 'test desc'},
]
const hashMap = {}
for (const { category, amount } of userData) {
if (hashMap[category]) {
hashMap[category].total += amount
} else {
hashMap[category] = { name: category, total: amount }
}
}
const output = Object.values(hashMap)
console.log(output)

Related

Calculate amounts in grouped array in JavaScript

I'm trying to generate a new array of people with their total amounts. I was able to filter out people based amounts. Could anyone please help?
mockData = [{
name: 'John',
title: 'Gas',
amount: 20.10
}, {
name: 'John',
title: 'Taco bell',
amount: 4.10
}, {
name: 'Doe',
title: 'Food',
amount: 30.50
}, {
name: 'Doe',
title: 'Groceries',
amount: 10.20
}, {
name: 'Doe',
title: 'Paint',
amount: 5
}];
const distinctItems = [...new Map(mockData.map(item => [item.name, item])).values()].map(({
name
}) => name);
const filterTotals = (expenses, person) =>
expenses.filter(({
name
}) => name === person)
const result = distinctItems.map((name) => filterTotals(mockData, name));
console.log(result)
The end result I'm expecting is
[{name: 'John', total: 24.20}, {name: 'Doe', total: 45.7}]
You could take a single loop with the data and add amount to the same name.
const
data = [{ name: 'John', title: 'Gas', amount: 20.10 }, { name: 'John', title: 'Taco bell', amount: 4.10 }, { name: 'Doe', title: 'Food', amount: 30.50 }, { name: 'Doe', title: 'Groceries', amount: 10.20 }, { name: 'Doe', title: 'Paint', amount: 5 }],
result = Array.from(
data.reduce((m, { name, amount }) => m.set(name, (m.get(name) || 0) + amount), new Map),
([name, total]) => ({ name, total })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Reduce the array to an object with key as the name & value as the total amount for that name & then you can use Object.entries to iterate over the key & value
const mockData = [{
name: 'John',
title: 'Gas',
amount: 20.10
}, {
name: 'John',
title: 'Taco bell',
amount: 4.10
}, {
name: 'Doe',
title: 'Food',
amount: 30.50
}, {
name: 'Doe',
title: 'Groceries',
amount: 10.20
}, {
name: 'Doe',
title: 'Paint',
amount: 5
}];
const obj = mockData.reduce((map, obj) => {
const {
name,
amount
} = obj
map[name] = name in map ? map[name] + amount : amount
return map
}, {})
const result = Object.entries(obj).map(([key, value]) => ({
name: key,
total: value.toFixed(2)
}))
console.log('result', result)
mockData = [{
name: 'John',
title: 'Gas',
amount: 20.10
}, {
name: 'John',
title: 'Taco bell',
amount: 4.10
}, {
name: 'Doe',
title: 'Food',
amount: 30.50
}, {
name: 'Doe',
title: 'Groceries',
amount: 10.20
}, {
name: 'Doe',
title: 'Paint',
amount: 5
}];
const totalAmountMap = mockData.reduce((accumulator, { name, amount }) => {
accumulator[name] = (accumulator[name] || 0) + amount;
return accumulator;
}, {});
const totalAmountArray = Object.keys(totalAmountMap).map((name) => ({
name,
total: totalAmountMap[name]
}));
console.log(totalAmountArray);

How do I get the total sum of nested arrays in Reactjs?

I want to get the total price of nested arrays in a specific category e.g: Hot Drinks.
Here is a sample of what I have now, so I want to filter out and get the total price of Hot Drinks Category only.
[
{
totalPrice: 30,
_id: '6014fa4324e125599eaa72b5',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Breakfast',
name: 'food name 1',
price: 3,
qty: 1,
},
{
_id: '6014fa4324e125599eaa747s5',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 5,
},
{
_id: '6014fa4324e125599eaa74767',
category: 'Hot Drinks',
name: 'drink name 2',
price: 4,
qty: 2,
},
],
},
{
totalPrice: 23,
_id: '6014fa4324e125599eaa7276e',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 6,
},
],
},
]
You can apply a filter method on the array and then just add the values on the filtered array. Something like below:
let prod = [
{
totalPrice: 30,
_id: '6014fa4324e125599eaa72b5',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Breakfast',
name: 'food name 1',
price: 3,
qty: 1,
},
{
_id: '6014fa4324e125599eaa747s5',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 5,
},
{
_id: '6014fa4324e125599eaa74767',
category: 'Hot Drinks',
name: 'drink name 2',
price: 4,
qty: 2,
},
],
},
{
totalPrice: 23,
_id: '6014fa4324e125599eaa7276e',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 6,
},
],
},
];
function getPriceByCategory(category, products) {
let price = 0;
products.forEach(orders => {
orders.orderItems.filter(order => order.category == category).forEach(item => {
price += item.price;
});
});
return price;
}
const totalPrice = getPriceByCategory('Hot Drinks', prod);
alert(totalPrice);
Sample JS Fiddle: https://jsfiddle.net/sagarag05/qwzju53f/9/
const filterBy = 'Hot Drinks';
const items = [
{
totalPrice: 30,
_id: '6014fa4324e125599eaa72b5',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Breakfast',
name: 'food name 1',
price: 3,
qty: 1,
},
{
_id: '6014fa4324e125599eaa747s5',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 5,
},
{
_id: '6014fa4324e125599eaa74767',
category: 'Hot Drinks',
name: 'drink name 2',
price: 4,
qty: 2,
},
],
},
{
totalPrice: 23,
_id: '6014fa4324e125599eaa7276e',
orderItems: [
{
_id: '6014fa4324e125599eaa747ss',
category: 'Hot Drinks',
name: 'drink name 1',
price: 3,
qty: 6,
},
],
},
]
const sumOf = (items, filterBy) => {
let totalPrice = 0;
items.forEach(item => {
item.orderItems.forEach(orderItem => {
if (orderItem.category === filterBy) {
totalPrice += orderItem.price;
}
})
})
return totalPrice;
}
console.log(sumOf(items, filterBy))
let sum = 0;
allOrders.forEach(order => {
order.orderItems.forEach(item => {
if(item.category=='Hot Drinks') {
sum+ = item.price * item.qty
}});
});
sum has the total price for Hot Drinks
Assuming you named that information as data:
Generate a big array of all the "orderItems"
For each of those elements sum the price if the category is "Hot Drinks"
const totalPrice = data
.reduce((acc, { orderItems }) => [...acc, ...orderItems], [])
.reduce((acc, { category, price }) => category === "Hot Drinks" ? acc + price : acc, 0);
console.log(totalPrice); // 10
Use flatMap and reduce or alternatively using forEach and destructuring
const total = (arr, text) =>
arr
.flatMap(({ orderItems }) => orderItems)
.reduce((acc, { category, price }) =>
(acc + (category === text ? price : 0)), 0);
// alternatively
const total2 = (arr, text, acc = 0) => {
arr.forEach(({ orderItems }) =>
orderItems.forEach(
({ category, price }) => (category === text && (acc += price))
)
);
return acc;
};
const data = [
{
totalPrice: 30,
_id: "6014fa4324e125599eaa72b5",
orderItems: [
{
_id: "6014fa4324e125599eaa747ss",
category: "Breakfast",
name: "food name 1",
price: 3,
qty: 1,
},
{
_id: "6014fa4324e125599eaa747s5",
category: "Hot Drinks",
name: "drink name 1",
price: 3,
qty: 5,
},
{
_id: "6014fa4324e125599eaa74767",
category: "Hot Drinks",
name: "drink name 2",
price: 4,
qty: 2,
},
],
},
{
totalPrice: 23,
_id: "6014fa4324e125599eaa7276e",
orderItems: [
{
_id: "6014fa4324e125599eaa747ss",
category: "Hot Drinks",
name: "drink name 1",
price: 3,
qty: 6,
},
],
},
];
console.log(total(data, 'Hot Drinks'))
console.log(total2(data, 'Hot Drinks'))

JavaScript Array Filtering and Mapping Problem. How to do nesting filtering?

Suppose, I have an array.
const arr = [
{
category: 'Diner',
item: [
{name: 'Chicken Rice', price: 200},
{name: 'Mutton Rice', price: 300},
],
},
{
category: 'Breakfast',
item: [
{name: 'Tea Bisuit', price: 100},
{name: 'Bread Butter', price: 300},
{name: 'Green Tea', price: 80},
],
},
];
How can I filter the array according to the item name?
For example, how can I filter the array with the item name Green Tea?
Output must be like this:
arr = [
{
category: 'Breakfast',
item: [
{name: 'Green Tea', price: 80},
],
},
];
You could map through arr, with each element, filter item to which match the term
After that, filter the arr again to reject the elements whose item is empty
const arr = [ { category: "Diner", item: [ { name: "Chicken Rice", price: 200 }, { name: "Mutton Rice", price: 300 }, ], }, { category: "Breakfast", item: [ { name: "Tea Bisuit", price: 100 }, { name: "Bread Butter", price: 300 }, { name: "Green Tea", price: 80 }, ], }, ]
const term = "Green Tea"
const res = arr
.map((categoryAndItems) => ({
category: categoryAndItems.category,
item: categoryAndItems.item.filter((item) => item.name === term),
}))
.filter((categoryAndItems) => categoryAndItems.item.length > 0)
console.log(res)

How to extract and group an array by key

I am building an Angular 9 app.
In this app I have a dynamically fetched JSON array. I need to take the JSON array and then extract and group based upon the keys. Example array:
const collection = [{
title: 'Product A',
price: 234,
cost: 234
}, {
title: 'Product B',
price: 100,
cost: 200
}, {
title: 'Product C',
price: 344,
cost: 55
}, {
title: 'Product D',
price: 222,
cost: 332
}];
I can mange to extract individual keys but I want to have a method that takes any JSON array and then extract and group per key.
This is my code for extracting individual keys. I had to hard code the key name (title).
groupByKey(array, key) {
return array.map(a => a.title);
}
This is what I want to transform the original JSON array to:
[{
header: "title",
rows: ["Product A", "Product B", "Product C", "Product D"]
}, {
header: "price",
rows: [234, 100, 344, 222]
}, {
header: "cost",
rows: [234, 200, 55, 332]
}]
You can perform a reduce operation on the array, using an object to store all the values of each key.
const collection = [{
title: 'Product A',
price: 234,
cost: 234
}, {
title: 'Product B',
price: 100,
cost: 200
}, {
title: 'Product C',
price: 344,
cost: 55
}, {
title: 'Product D',
price: 222,
cost: 332
}];
function group(arr){
return Object.values(
arr.reduce((acc,curr)=>{
Object.entries(curr).forEach(([k,v])=>{
(acc[k] = acc[k] || {header: k, rows: []}).rows.push(v);
});
return acc;
}, {})
);
}
console.log(group(collection));
The simplest solution is:
const collection = [{
title: 'Product A',
price: 234,
cost: 234
}, {
title: 'Product B',
price: 100,
cost: 200
}, {
title: 'Product C',
price: 344,
cost: 55
}, {
title: 'Product D',
price: 222,
cost: 332
}];
const transform = inputArray => {
const headers = inputArray && inputArray[0] && Object.keys(inputArray[0]);
return headers.map(header =>
({header: header, rows: collection.map(item => item[header])}));
}
console.log(transform(collection));
Out:=>
[
{
header: 'title',
rows: [ 'Product A', 'Product B', 'Product C', 'Product D' ]
},
{
header: 'price',
rows: [ 234, 100, 344, 222 ]
},
{
header: 'cost',
rows: [ 234, 200, 55, 332 ]
}
]
You could do it non functional
const collection = [{
title: 'Product A',
price: 234,
cost: 234
}, {
title: 'Product B',
price: 100,
cost: 200
}, {
title: 'Product C',
price: 344,
cost: 55
}, {
title: 'Product D',
price: 222,
cost: 332
}]
const keys = Object.keys(collection[0]);
const arr = []
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const obj = {
header: null,
rows: []
};
for (let item of collection) {
obj['header'] = key;
obj['rows'].push(item[key])
}
arr.push(obj)
}
console.log(arr)
Approach using a Map, a simple loop and spread of Map#values()
const map = new Map(Object.keys(collection[0]).map(k => [k, {header:k, rows:[]}]));
collection.forEach(el =>Object.entries(el).forEach(([k, v]) => map.get(k).rows.push(v)))
console.log( [...map.values()])
<script>
const collection=[{title:"Product A",price:234,cost:234},{title:"Product B",price:100,cost:200},{title:"Product C",price:344,cost:55},{title:"Product D",price:222,cost:332}];
</script>
This function will simply take in the entire collection and does not need a key. It assumes that each object in the collection has the same keys.
const groupByKey = (collection) => {
const result = []
// return empty array if collection is empty
if (collections.length === 0) return result
// assumes that all json in the collection will have the same properties
const keys = Object.keys(collection[0])
keys.forEach(key => {
const row = []
collection.forEach(json => {
row.push(json[key])
})
const formattedJSON = {
header: key,
row: row
}
result.push(formattedJSON)
})
return result
}
As a note, in your groupByKey function, to get the key dynamically, you can do:
a[title]
because a.title will literally look for a key called "title".

Merge & Group Two Javascript array of objects and Group

I have two arrays of objects. One array contains list of items, another array contains list of categories. I want to create a new array based on categoryIds. I tried using lodash. But, couldn't get the correct solution.
I can do this using looping. But, I am looking for more clean approach.
var items = [
{
id: '001',
name: 'item1',
description: 'description of item1',
categoryId: 'cat1'
},
{
id: '002',
name: 'item2',
description: 'description of item2',
categoryId: 'cat2'
},
{
id: '003',
name: 'item3',
description: 'description of item3',
categoryId: 'cat1'
},
{
id: '004',
name: 'item4',
description: 'description of item4'
}
];
var categories = [
{
id: 'cat1',
name: 'Category1'
},
{
id: 'cat2',
name: 'Category2'
}
];
Expected output
[
{
categoryId: 'cat1',
name: 'Category1',
items: [
{
id: '001',
name: 'item1',
description: 'description of item1',
categoryId: 'cat1'
},
{
id: '003',
name: 'item3',
description: 'description of item3',
categoryId: 'cat1'
}
]
},
{
categoryId: 'cat2',
name: 'Category2',
items: [
{
id: '002',
name: 'item2',
description: 'description of item2',
categoryId: 'cat2'
}
]
},
{
categoryId: '',
name: '',
items: [
{
id: '004',
name: 'item4',
description: 'description of item4'
}
]
}
]
https://jsfiddle.net/sfpd3ppn/
Thanks for the help
The following does the trick:
var items = [{ id: '001', name: 'item1', description: 'description of item1', categoryId: 'cat1' }, { id: '002', name: 'item2', description: 'description of item2', categoryId: 'cat2' }, { id: '003', name: 'item3', description: 'description of item3', categoryId: 'cat1' }, { id: '004', name: 'item4', description: 'description of item4' } ];
var categories = [ { id: 'cat1', name: 'Category1' }, { id: 'cat2', name: 'Category2' } ];
var output = categories.concat([{id:'',name:''}]).map(function(v) {
return {
categoryId: v.id,
name: v.name,
items: items.filter(function(o) {
return o.categoryId === v.id || !o.categoryId && !v.id;
})
};
});
console.log(output);
I start by using .concat() to create a new categories array that holds the original categories items plus an "empty" category. Then I .map() that array to return category objects with your desired output structure, each of which has an items array that is produced by .filter()ing the original items array.
(Note that the items arrays within the output contain references to the same objects that were in the original items input, not copies of them. If you wanted copies you could add another .map() after the .filter().)
You can accomplish the desired result using a reduce. We are going to start with the original categories array and reduce the items array into it.
var items = [
{ id: '001', name: 'item1', description: 'description of item1', categoryId: 'cat1' },
{ id: '002', name: 'item2', description: 'description of item2', categoryId: 'cat2' },
{ id: '003', name: 'item3', description: 'description of item3', categoryId: 'cat1' },
{ id: '004', name: 'item4', description: 'description of item4' }
];
var categories = [
{ id: 'cat1', name: 'Category1' },
{ id: 'cat2', name: 'Category2' }
];
// Lets add the empty category at the beginning. This simplifies the logic.
categories.push({ id: '', name: '' });
// This is a function that will return a function to be used as a filter later on
function createFilter (category) {
return function (item) {
return item.id === category;
};
}
var mergedSet = items.reduce(function (previous, current) {
// Get the category ID of the current item, if it doesn't exist set to empty string
var categoryId = current.categoryId || '';
// Find the cateogry that matches the category ID
var category = previous.find(createFilter(categoryId));
// If the items property doesn't exists (we don't have any items), create an empty array
if (!category.items) { category.items = []; }
// Add the item the category
category.items.push(current);
// Return the current value that will be used in the next iteration.
// Note, the initial value of previous will be the intial value of categories.
return previous;
}, categories);
console.log(mergedSet);
/* Output
[
{ id: 'cat1',
name: 'Category1',
items:
[ { id: '001',
name: 'item1',
description: 'description of item1',
categoryId: 'cat1' },
{ id: '003',
name: 'item3',
description: 'description of item3',
categoryId: 'cat1' }
]
},
{ id: 'cat2',
name: 'Category2',
items:
[ { id: '002',
name: 'item2',
description: 'description of item2',
categoryId: 'cat2'
}
]
},
{ id: '',
name: '',
items:
[ { id: '004',
name: 'item4',
description: 'description of item4' } ] }
]
*/
Assuming the variables categories and items are assigned as you defined above:
const keyedCategories = _(categories)
.concat({ id: '', name: '' })
.keyBy('id')
.value();
const groupedItems = _.groupBy(items, (item) => _.get(item, 'categoryId', ''));
const result = _.reduce(groupedItems, (acc, value, key) => {
const { id: categoryId, name } = keyedCategories[key];
return _.concat(acc, { categoryId, name, items: value });
}, []);

Categories