Calculate amounts in grouped array in JavaScript - 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);

Related

How to compare the sameness of object entries of 2 arrays and how to create a merger of both objects with custom properties of the found same object?

I am trying to compare 2 objects by their property and the values Strictly using forloop. If the value of the "name" or another property matches up with each other, I want to push the property and value to value3.
Once value3 is logged, I want the response like this:
{
name: 'dog',
surname: 'good',
skills: 'programming',
age: '22'
},
{
name: 'cat',
surname: 'soft',
skills: 'engineer'
age: '12'
},
{
name: 'elephant',
surname: 'big',
skills: 'programming'
age: '23'
}
Here is the code:
var values1 = [
{
name: 'dog',
surname: 'good',
skills: 'programming'
},
{
name: 'cat',
surname: 'soft',
skills: 'engineer'
},
{
name: 'elephant',
surname: 'big',
skills: 'programming'
}
]
var values2 = [
{
name: 'cat',
food: 'fish',
age: '12'
},
{
name: 'elephant',
food: 'leafs',
age: '13'
},
{
lastname: 'dog',
food: 'treats',
age: '22'
}
]
// push into this empty object array
var values3 = [{}]
console.log(values3)
The most generic approach which fulfills all of the OP's requirements should be based on Array.prototype.reduce. Its advantage comes with utilizing the additionally passed optional initial value as kind of configurable collector/accumulator object which will carry all the needed additional information / functions / result. Thus one can provide a reusable function with a customizable context/environment which one can adapt to ones needs.
var values1 = [{
name: 'dog',
surname: 'good',
skills: 'programming',
}, {
name: 'cat',
surname: 'soft',
skills: 'engineer',
}, {
name: 'elephant',
surname: 'big',
skills: 'programming',
}];
var values2 = [{
name: 'cat',
food: 'fish',
age: '12'
}, {
name: 'elephant',
food: 'leafs',
age: '13'
}, {
lastname: 'dog',
food: 'treats',
age: '22'
}];
function mergeItemsOfSameEntry(collector, item) {
const {
getSameEntryValue,
getMergeSubType,
comparisonItems,
result
} = collector;
const itemValue = getSameEntryValue(item);
const comparisonItem = comparisonItems
.find(cItem => getSameEntryValue(cItem) === itemValue);
if (comparisonItem !== null) {
result.push({
...item,
...getMergeSubType(comparisonItem),
});
}
return collector;
}
const values3 = values1.reduce(mergeItemsOfSameEntry, {
getSameEntryValue: item => item.name ?? item.lastname,
getMergeSubType: ({ age }) => ({ age }),
comparisonItems: values2,
result: [],
}).result;
console.log({ values3 });
.as-console-wrapper { min-height: 100%!important; top: 0; }
If you just want the key and value pair, you can do something like this:
var values1 = [
{
name: 'dog',
surname: 'good',
skills: 'programming'
},
{
name: 'cat',
surname: 'soft',
skills: 'engineer'
},
{
name: 'elephant',
surname: 'big',
skills: 'programming'
}
]
var values2 = [
{
name: 'cat',
food: 'fish',
age: '12'
},
{
name: 'elephant',
food: 'leafs',
age: '13'
},
{
lastname: 'dog',
food: 'treats',
age: '22'
}
]
// push into this empty object array
var values3 = [];
values1.forEach(eachValue1Obj => {
const keys = Object.keys(eachValue1Obj);
keys.forEach(eachKey => {
values2.forEach(eachValue2Obj => {
if (
eachValue1Obj[eachKey] &&
eachValue2Obj[eachKey] &&
eachValue1Obj[eachKey] === eachValue2Obj[eachKey]
) {
const x = {
key: eachKey,
value: eachValue1Obj[eachKey]
};
values3.push(x)
}
})
})
})
console.log('Values 3 Array ==>', values3);

Merge item in array of object with the same ID on Javascript

this is how the object look:
let data = [
{
brandId: '12345',
brand: 'Adidas',
item: {
name: 'Adidas 1',
price: '200',
},
},
{
brandId: '12345',
brand: 'Adidas',
item: {
name: 'Adidas 2',
price: '230',
},
},
{
brandId: '7878',
brand: 'Nike',
item: {
name: 'Nike 1',
price: '305',
},
}
];
i want the item object will merge if the object have the same brandID :
let data = [
{
brandId: '12345',
brand: 'Adidas',
item: [
{
name: 'Adidas 1',
price: '200',
},
{
name: 'Adidas 2',
price: '230',
},
],
},
{
brandId: '7878',
brand: 'Nike',
item: {
name: 'Nike 2',
price: '316',
},
},
];
is there any javascript syntax or method to do this ? and with an explanation will be very nice, Thank You
(Assuming that your output is just a typo and name/price doesn't actually changes) You can use array reduce
let data = [
{
brandId: '12345',
brand: 'Adidas',
item: {
name: 'Adidas 1',
price: '200',
},
},
{
brandId: '12345',
brand: 'Adidas',
item: {
name: 'Adidas 2',
price: '230',
},
},
{
brandId: '7878',
brand: 'Nike',
item: {
name: 'Nike 1',
price: '305',
},
}
];
const mergedItems = data.reduce((acc, curr) => {
// check if current exist on the accumulator
const exist = acc.find(brand => brand.brandId === curr.brandId);
// if it does, add the item on it
if (exist) {
return acc.map((brand) => {
if (brand.brandId === exist.brandId) {
return {
...brand,
item: brand.item.concat(curr.item),
}
}
})
}
// if it doesnt, add it on accumulator, and make the item array
return acc.concat({
...curr,
item: [
curr.item
]
})
})
(I wrote the code manually and not tested)
You can simply achieve this result using Map
let data = [
{
brandId: "12345",
brand: "Adidas",
item: {
name: "Adidas 1",
price: "200",
},
},
{
brandId: "12345",
brand: "Adidas",
item: {
name: "Adidas 2",
price: "230",
},
},
{
brandId: "7878",
brand: "Nike",
item: {
name: "Nike 1",
price: "305",
},
},
];
const dict = new Map();
data.forEach((o) => {
dict.get(o.brandId)
? dict.get(o.brandId).item.push(o.item)
: dict.set(o.brandId, { ...o, item: [o.item] });
});
const result = [];
for (let [k, v] of dict) {
v.item.length === 1 ? result.push({ ...v, item: v.item[0] }) : result.push(v);
}
console.log(result);
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }

Frequency Counter in array of objects for certain key

I have an array of objects with students name which are getting repeated often. I want to create a frequency counter of the students name with the number of total counts.
Array :
arr = [
{
name: 'Akshay',
age: '15',
},
{
name: 'Rajat',
age: '14',
},
{
name: 'Akshay',
age: '16',
},
{
name: 'Sam',
age: '12',
},
{
name: 'Akshay',
age: '11',
},
{
name: 'Rajat',
age: '17',
},
{
name: 'Akshay',
age: '12',
},
{
name: 'Sam',
age: '18',
},
{
name: 'Sam',
age: '19',
}
]
I want to get the result like this in an array
result = [
{
name: 'Akshay',
count: 4
},
{
name: 'Rajat',
count: 2
},
{
name: 'Sam',
count: 3
},
]
I tried the below solution but it's not working properly -
const result = arr.reduce((counter,item) => {
var getItem = item.name;
counter[getItem] = counter.hasOwnProperty(getItem) ? counter[getItem] : 1;
return counter;
})
Please help.
You need to pass an accumulator in the reduce. In this case pass an empty object as an accumulator , so the accumulator will look like
{
name:{
name:someName,
count:someCount
},
name2:{
name:someName,
count:someCount
}
}
Once the accumulator is successfully populated, then use Object.values to get an array
const arr = [{
name: 'Akshay',
age: '15',
},
{
name: 'Rajat',
age: '14',
},
{
name: 'Akshay',
age: '16',
},
{
name: 'Sam',
age: '12',
},
{
name: 'Akshay',
age: '11',
},
{
name: 'Rajat',
age: '17',
},
{
name: 'Akshay',
age: '12',
},
{
name: 'Sam',
age: '18',
},
{
name: 'Sam',
age: '19',
}
]
const result = arr.reduce((counter, item) => {
var getItem = item.name;
if (counter.hasOwnProperty(getItem)) {
counter[getItem].count += 1;
} else {
counter[getItem] = {
name: getItem,
count: 1
};
}
return counter;
}, {});
console.log(Object.values(result))
Try this: (you have been missing the initial value)
const count = arr.reduce((prev, cur) => {
prev[cur.name] = (prev[cur.name] || 0) + 1;
return prev;
}, {});
console.log(count);

Grouping and summing array objects

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)

Return object(s) in array with highest property value per group

I have an array containing several objects similar to the following:
{person: {name: "Steve", id: 1}, role: 1}
{person: {name: "Phil", id: 2}, role: 1}
{person: {name: "Steve", id: 1}, role: 3}
{person: {name: "Phil", id: 2}, role: 6}
My intention is to return an array of the same type, but I'd like to return only one object per "person" with their highest role.
I understand the following will give me a single object with the highest role.
array.reduce((prev, cur) => prev.role > cur.role ? prev : cur);
How do I return each unique person and their corresponding highest role as a new array?
Like so:
{person: {name: "Steve", id: 1}, role: 3}
{person: {name: "Phil", id: 2}, role: 6}
You need to collect the objects and if you have already one with the same id check the role and take the greater one.
var data = [{ person: { name: "Steve", id: 1 }, role: 1 }, { person: { name: "Phil", id: 2 }, role: 1 }, { person: { name: "Steve", id: 1 }, role: 3 }, { person: { name: "Phil", id: 2 }, role: 6 }],
grouped = data.reduce((r, o) => {
var index = r.findIndex(({ person: { id } }) => id === o.person.id);
if (index === -1) {
r.push(o);
return r;
}
if (r[index].role < o.role) {
r[index] = o;
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Group the items by person.id with Array.reduce(), but for each id store only the object with the highest role. Convert back to array using Object.values():
const data = [{ person: { name: "Steve", id: 1 }, role: 1 }, { person: { name: "Phil", id: 2 }, role: 1 }, { person: { name: "Steve", id: 1 }, role: 3 }, { person: { name: "Phil", id: 2 }, role: 6 }];
const result = Object.values(data.reduce((r, o) => {
const id = o.person.id;
if(!r[id] || r[id].role < o.role) r[id] = o;
return r;
}, []));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use Array.prototype.reduce and build out an object literal like below. Use the person.name property value as the object key and then just update the role value if you find a higher value:
var people = [{
person: {
name: "Steve",
id: 1
},
role: 1
}, {
person: {
name: "Phil",
id: 2
},
role: 1
}, {
person: {
name: "Steve",
id: 1
},
role: 3
}, {
person: {
name: "Phil",
id: 2
},
role: 6
}];
var highRoleByPerson = people.reduce((accum, el) => {
if (accum[el.person.name]) {
if (el.role > accum[el.person.name].role) {
accum[el.person.name].role = el.role;
}
} else {
accum[el.person.name] = {
person: {
name: el.person.name,
id: el.person.id
},
role: 0
};
}
return accum;
}, {});
console.log(highRoleByPerson);

Categories