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)
Related
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)
`
Please help with the task.
There is an array of objects:
[
{id: 0, title: 'title_1', step: 3},
{id: 1, title: 'title_2', step: 2},
{id: 2, title: 'title_3', step: 3},
{id: 3, title: 'title_4', step: 1},
{id: 4, title: 'title_5', step: 2},
...
]
How can I turn this array into this one?
[
[
{id: 0, title: 'title_1', step: 3},
{id: 2, title: 'title_3', step: 3},
],
[
{id: 3, title: 'title_4', step: 1},
],
[
{id: 1, title: 'title_2', step: 2},
{id: 4, title: 'title_5', step: 2},
],
...
]
That is, split it into subarrays of objects, using the step property
thanks
You could use Array.prototype.reduce() method to get the sub arrays by the step property. Traverse the array using reduce method and make step as a key as well as create sub array based on that key(step). At last, get all the values using Object.values() method.
const data = [
{ id: 0, title: 'title_1', step: 3 },
{ id: 1, title: 'title_2', step: 2 },
{ id: 2, title: 'title_3', step: 3 },
{ id: 3, title: 'title_4', step: 1 },
{ id: 4, title: 'title_5', step: 2 },
];
const ret = Object.values(
data.reduce((prev, c) => {
const p = prev;
const key = c.step;
p[key] = p[key] ?? [];
p[key].push(c);
return p;
}, {})
);
console.log(ret);
This version is algorithmically similar to the one from mr hr. It's just written in the mutation-free style I prefer. It is less performant, but to my mind easier to understand. But YMMV.
const groupArrayBy = (prop) => (arr) =>
Object .values (
arr .reduce ((a, x) => ({...a, [x [prop]]: [... (a [x [prop]] || []), x]}), {})
)
const input = [{id: 0, title: 'title_1', step: 3}, {id: 1, title: 'title_2', step: 2}, {id: 2, title: 'title_3', step: 3}, {id: 3, title: 'title_4', step: 1}, {id: 4, title: 'title_5', step: 2}]
console .log (groupArrayBy ('step') (input))
.as-console-wrapper {max-height: 100% !important; top: 0}
We write a generic function groupArrayBy that takes a property name and returns a function that groups an array into subarrays sharing particular values for that property.
You can use it directly as we do above, or you can write a function that partially applies the first parameter, such as
const byStep = groupArrayBy ('step')
// ... later
const results = byStep (input)
I have arrays of objects that look like this:
const array1 = [{id: 1, name: "John"}, {id: 2, name: "Mary"}]
const array2 = [{id: 1, name: "John"}, {id: 3, name: "Phil"}, {id: 4, name: "Sarah"}]
How can I add unique objects from array2 to array1 so it looks like this:
const array1 = [{id: 1, name: "John"}, {id: 2, name: "Mary"}, {id: 3, name: "Phil"}, {id: 4, name: "Sarah"}]
Lodash implementations are permitted. Thanks a lot.
You can use _.unionBy() function to merge unique objects from arrays.
const array1 = [{id: 1, name: "John"}, {id: 2, name: "Mary"}];
const array2 = [{id: 1, name: "John"}, {id: 3, name: "Phil"}, {id: 4, name: "Sarah"}];
console.log(_.unionBy(array1, array2, 'id'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
Using native array functions you can get the desired result as follows:
Concat both arrays first using .concat()
Use .reduce() to create the resultant object having ids as keys and values as relevant object. If already added an object then skip the others with same ids.
Use Object.values() to get an array of the objects from the resultant object.
Demo:
const array1 = [{id: 1, name: "John"}, {id: 2, name: "Mary"}],
array2 = [{id: 1, name: "John"}, {id: 3, name: "Phil"}, {id: 4, name: "Sarah"}];
const result = Object.values(
array1.concat(array2).reduce((r, c) => (r[c.id] = r[c.id] || c, r), {})
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can also do it in one line via native Map object and reduce:
const arr1 = [{id: 1, name: "John"}, {id: 2, name: "Mary"}]
const arr2 = [{id: 1, name: "John"}, {id: 3, name: "Phil"}, {id: 4, name: "Sarah"}]
const result = [...[...arr1, ...arr2]
.reduce((r, c) => (r.set(c.id, c), r), new Map()).values()]
console.log(result)
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)
});
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.