How to add value of similar objects keys in es6 - javascript

Like this is my array of objects:
var x = [
{_id: 1, total: 25},
{_id: 1, total: 22},
{_id: 2, total: 4},
{_id: 2, total: 32},
{_id: 3, total: 56},
{_id: 4, total: 21},
{_id: 4, total: 58},
]
Now I want to achieve all total sum of similar object keys like this
[
{_id: 1, total: 47},
{_id: 2, total: 36},
{_id: 3, total: 25},
{_id: 4, total: 79},
]
Can anyone suggest how to do this on es6

Use reduce. reduce is an array method that can transform an array into something else, i.e. another array that can have different length. map will always return an array with the same number of elements. And filter can return an array that has less elements but the elements will be unchanged.
Reduce gives you the more flexible behavior. You can change the elements and you can store them in whatever way you like.
const result = x.reduce((acc, el) => {
const index = acc.findIndex(({_id}) => el._id === _id);
if (index > -1) {
acc[index].total += el.total;
} else {
acc.push({...el});
}
return acc;
}, [])
console.log(result);
In case if this code would run often and on large arrays, you can use more performant but a little more complex solution where we use a hash table to store the data:
var x = [
{_id: 1, total: 25},
{_id: 1, total: 22},
{_id: 2, total: 4},
{_id: 2, total: 32},
{_id: 3, total: 56},
{_id: 4, total: 21},
{_id: 4, total: 58},
]
const temp = {};
for (const el of x) {
temp[el._id] = (temp[el._id] || 0) + el.total;
}
const result = Object.entries(temp).map(([_id, total]) => ({_id, total}));
console.log(result);
But before starting optimizing you should always check if it's worth doing by running perf tools.

Related

array reduce amount value into string format

I have an array of items, where i need to get a string of each product price.
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}]
const output = input?.reduce((acc, curr) => {
acc += `$${curr.amount}+`;
return acc;
}, '')
console.log(output)
Expected output is $20+$40+$90
But when i am trying this code i am getting the sum as $150 and i don't want to have + at the last if there are no more items.
Why Array.reduce()? This is a classic example for Array.map():
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}]
const expression = input.map(
({ amount }) => `$${amount}` // destructure the object, keep only .amount
).join('+');
console.log(expression);
Read about destructuring in the JavaScript documentation.
You can use map to extract the values followed by a join to create the string.
input.map(i => `$${i.amount}`).join('+')
Use split, and your code almost works
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}]
const output = input?.reduce((acc, curr) => {
acc.push(curr.amount + "$");
return acc;
}, []).join("+")
console.log(output)
const input = [{ id: 1, amount: 20 }, { id: 2, amount: 40 }, { id: 3, amount: 90 }]
const output = input?.slice(1).reduce((acc, curr) => {
acc += `+$${curr.amount}`;
return acc;
}, input.length ? `$${input[0].amount}` : '');
console.log(output)
with minimum manipulation
To add to the answers, we can use the currentIndex in the callback function in reduce as the third argument.
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}, {id: 4, amount: 55}]
const output = input?.reduce((acc, curr, index) => {
acc += `$${curr.amount}`;
if (index < input.length - 1) acc += '+'
return acc;
}, '')
console.log(output)
`

How to sort res.data by _id?

My res.data always randomly sorted, how to sort res.data by _id ?
const [income, setIncome] = useState([]);
const [perc, setPerc] = useState(0);
useEffect(() => {
const getIncome = async () => {
try {
const res = await userRequest.get("orders/income");
setIncome(res.data);
setPerc((res.data[1].total * 100) / res.data[0].total - 100);
console.log(res.data);
} catch {}
};
getIncome();
}, []);
console.dev:
0: {_id: 10, total: 990}
1: {_id: 11, total: 20}
2: {_id: 6, total: 448}
3: {_id: 9, total: 700}
4: {_id: 8, total: 100}
5: {_id: 7, total: 900}
You can do this to sort based on it:
res.data.sort((x, y) => x._id - y._id)
This will sort in ascending order based on the _id attribute for each item of the res.data array.
You can use Array.sort for that:
const data = [
{_id: 10, total: 990},
{_id: 11, total: 20},
{_id: 6, total: 448},
{_id: 9, total: 700},
{_id: 8, total: 100},
{_id: 7, total: 900}
]
const sorted = data.sort((a, b) => a._id - b._id)
console.log(data)
In this example I use subtraction "hack" do determinate order. It works only for numbers, if you want to compare strings, for example, you can pass custom compare function (compareFunction(a, b)) that returns:
> 0 - sort a after b
< 0 - sort a before b
=== 0 - keep original order of a and b
You can use the built-in sort function like this:
myArray = [{_id: 10, total: 990},
{_id: 11, total: 20},
{_id: 6, total: 448},
{_id: 9, total: 700},
{_id: 8, total: 100},
{_id: 7, total: 900}];
// now myArray is sorted "in place" in a descending order.
myArray.sort((a, b) => a._id - b._id);
// you can flip it so it is ordered in an ascending order:
myArray.sort((a, b) => b._id - a._id);
Notice that sorting by id is not recommended because there is no business logic related to the order of ids. Why should item 1 be shown before item 2?
The order should depend on a business-related field, for example, modified date.
If possible, you should sort the data directly from the API.
Otherwise, you can use the Array#sort function once you get the data
const arr = [{
_id: 10,
total: 990
}, {
_id: 11,
total: 20
}, {
_id: 6,
total: 448
}, {
_id: 9,
total: 700
}, {
_id: 8,
total: 100
}, {
_id: 7,
total: 900
}]
arr.sort((a, b) => a._id - b._id)
console.log(arr)

Summing Records and Consolidating Based on IDs in Array

I have an array of records that contain objects, each with an id and an amount. Now, in this array, some elements have the same id. What I need to do is iterate over this array, and first, sum up the values for records that have the same id, and then return just one record when that is the case - so I end up with an array with only unique ids -- each one also containing an amount property.
In other words, I want to take this array:
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10}
];
... and produce this array:
const transformedRecords = [
{id: 1, amount: 30},
{id: 2, amount: 10},
{id: 3, amount: 0}
];
I've thought about using a for-of loop for this, but that might bet pretty verbose, and I'm guessing there's a more succinct way to accomplish this - perhaps with reduce()? What's an elegant way to approach this, preferably using es6+ syntax?
Use Array.reduce, for each iteration, check if you have an object with the current id in the accumulator, if you do, add the amounts, if not, push the current object to the accumulator :
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10},
{id: 4, amount: -10},
{id: 4, amount: -10}
];
const result = records.reduce((acc, curr) => {
const ndx = acc.findIndex(e => e.id === curr.id);
if(ndx > -1) {
acc[ndx].amount += curr.amount
}
else{
acc.push(curr)
}
return acc;
}, [])
console.log(result)
You can use reduce() to create an object and then use map() on its entries to create array of objects back
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10}
];
const res = Object.entries(records.reduce((ac, a) => {
ac[a.id] = (a[a.id] || 0) + a.amount;
return ac
}, {})).map(([id, amount]) => ({id, amount: amount < 0 ? 0 : amount}))
console.log(res)

Javascript create array from existing split on property value

I'm trying to iterate over an existing array with of objects with a 'quantity' property and rebuild it by a control value.
let cart = [{id: 1, name: 'Pizza', quantity: 5, specialId: 0},
{id: 2, name: 'Burger', quantity: 2, specialId: 0}];
I have a control of 3 items i.e. for every 3 items you get a discount so I'd like to reconstitute the cart array as follows:
cart = [{id: 1, name: 'Pizza', quantity: 3, specialId: 1},
{id: 2, name: 'Pizza', quantity: 2, specialId: 2},
{id: 3, name: 'Burger', quantity: 1, specialId: 2},
{id: 4, name: 'Burger', qty: 1, specialId: 0}]
I've looked at several ways of doing this mostly around creating a new array of single quantity items and then creating another final array but surely that isn't very efficient?
I'd appreciate any pointers. I have a horrible feeling I'm missing something simple and have stared at this too long.
If I understand correctly the amount of three is ignorant of the type of product, so the second batch of three (in your example) consists of 2 pizzas and 1 burger.
The specialId seems to be unique and non-zero for every complete set of three (where every item in that set shares that specialId value), and zero for any remaining item(s).
Finally, it seems that the id in the result is unrelated to the input, but just an incremental number.
Here is how you could do that:
function splitBy(cart, size) {
const result = [];
let quantity = 0;
let grab = size;
let specialId = 1;
let id = 1;
for (let item of cart) {
for (quantity = item.quantity; quantity >= grab; quantity -= grab, grab = size, specialId++) {
if (result.length && !result[result.length-1].specialId) result[result.length-1].specialId = specialId;
result.push(Object.assign({}, item, {quantity: grab, specialId, id: id++}));
}
if (quantity) result.push(Object.assign({}, item, {quantity, specialId: 0, id: id++}));
grab = size - quantity;
}
return result;
}
const cart = [{id: 1, name: 'Pizza', quantity: 5, specialId: 0},
{id: 2, name: 'Burger', quantity: 2, specialId: 0}];
const result = splitBy(cart, 3)
console.log(result);
Basically you have two options.
loop over the current cart, and if the quantity is over 3, split it to two, and push them both.
split the array, and then merge it together.
My guess is to go with the first option, doing something like this:
var cart = [{id: 1, name: 'Pizza', quantity: 5, specialId: 0},
{id: 2, name: 'Burger', quantity: 2, specialId: 0}];
var a = [];
cart.forEach(x => {
if (x.quantity > 3) {
let temp = {...x};
temp.quantity = 3;
a.push(temp);
x.quantity -= 3;
}
a.push(x)
});

Is there any lodash function to achieve this?

I have an array
var a = [
{id: 1, item: 3},
{id: 1, item: 4},
{id: 1, item: 5},
{id: 2, item: 6},
{id: 2, item: 7},
{id: 3, item: 8}
]
I need output like this:
[{id: 1, items: [3, 4, 5]}, {id: 2, items: [6,7]}, {id: 3, items: [8]}]
Here's a solution that first groups by id and then maps across the groupings to get the required collection:
let result = _(a)
.groupBy('id')
.map( (group ,id) => ({id: id, items: _.map(group, 'item')}))
.value()
It's pretty ugly, but then other answers are not pretty either
var a = [
{id: 1, item: 3},
{id: 1, item: 4},
{id: 1, item: 5},
{id: 2, item: 6},
{id: 2, item: 7},
{id: 3, item: 8}
];
var ret = _.chain(a)
.groupBy(elt => elt.id)
.mapValues(elt => _.reduce(elt, (acc, sub) => acc.concat(sub.item),[]))
.map((value, key) => ({id: key, items:value}))
.value();
console.log(ret);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>

Categories

Resources