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)
`
Related
I am trying to count hours worked for an individual id dynamically. Currently, if I want to count the hours for id 1 for example I am having to hard code the id value and do the count. example below. I am using datatables so there will eventually be hundreds of rows
var array = [{id: 1, hours: 10},{id: 1, hours: 12}, {id: 2, hours: 6}, {id: 2, hours: 11} {id: 3, hours: 12}, {id: 3, hours: 8}]
var array = dataTable.rows().data().toArray();
array.filter((h) => {
if (h.id == '1' && (count += h.hours) > 5) {
'do something'
}
else {'do something'}
})
How do I count the hours of each 'id' dynamically without hard coding the id value thank you
Looks like you could use .map() here over filter I would wrap your filter / map into a function with an argument of id that way you can pass the ID everytime you call the function, without hard coding it.
var array = [{id: 1, hours: 10},{id: 1, hours: 12}, {id: 2, hours: 6}, {id: 2, hours: 11} {id: 3, hours: 12}, {id: 3, hours: 8}]
const countHours = (id) => {
array.map(item => {
if (item.id === id && item.hours > 5) {
return 'do something'
}
else { return 'do something'}
})
}
// call it like so...
countHours(1)
You could wrap this logic into a function that receives the id argument:
function hoursById (id) {
let count = 0;
const array = dataTable.rows().data().toArray();
array.forEach((h) => {
if (h.id == id && (count += h.hours) > 5) {
'do something'
} else {
'do something'}
})
return count;
}
Then you can invoke the function as
hoursById('1') // return count for id 1
hoursById('2') // return count for id 2
You could make a function to filter the array and use reduce to sum all the 'hours' values.
var arr = [{id: 1, hours: 10},{id: 1, hours: 12}, {id: 2, hours: 6}, {id: 2, hours: 11}, {id: 3, hours: 12}, {id: 3, hours: 8}]
function sumIdHours(id){
var hourSum = arr.filter(x => x.id === id).reduce((a, b) => a + b.hours, 0)
return hoursSum
}
//e.g.
sumIdHours(1)
I have an array of n objects in it. Each object has names as id, quantity, price and total. All of these have significance in solving this problem.
How can I create new arrays where sum of all total of their objects doesn't exceed 150.
My array:
let array = [{id: 1, quantity: 5, price: 10, total: 50}, {id: 2, quantity: 3, price: 100, total: 300}]
Expected result:
array1 = [{id: 1, quantity: 5, price: 10, total: 50}, {id: 2, quantity: 1, price: 100, total: 100}]
array2 = [{id: 2, quantity: 1, price: 100, total: 100}]
array3 = [{id: 2, quantity: 1, price: 100, total: 100}]
Conditions:
As mentioned, sum of totals in new arrays mustn't exceed 150
value of total must always be product of quantity and price in that object
object must keep dividing itself into new objects with smaller quantities until above conditions are met
quantity must be an integer
i tried this.
const itemsFinals = [];
const maxTotal = 150;
let totalGroup = 0;
for (i = 0; i < itemComprobantes.length; i++) {
if((itemComprobantes[i].total + totalGroup) < maxTotal){
itemsFinals.push(itemComprobantes[i]);
totalGroup += itemComprobantes[i].total;
}
}
let itemComprobantes = [
{id: 1, quantity: 5, price: 10, total: 50},
{id: 2, quantity: 1, price: 100, total: 100},
{id: 3, quantity: 1, price: 100, total: 100},
{id: 4, quantity: 1, price: 100, total: 30},
{id: 5, quantity: 1, price: 100, total: 80}
]
const itemsFinals = {}
const maxTotal = 150
let totalGroup = 0
let group = 1
for (i = 0; i < itemComprobantes.length; i++) {
if((itemComprobantes[i].total + totalGroup) > maxTotal){
group += 1
totalGroup = 0
}
if (itemsFinals['group '+group] === undefined) {
itemsFinals['group '+group] = []
}
itemsFinals['group '+group].push(itemComprobantes[i])
totalGroup += itemComprobantes[i].total
}
console.log(itemsFinals)
Here is my answer base on my comments.
Here is one way to do it. I know its long but give it a try
let array = [{id: 1, quantity: 5, price: 10, total: 50}, {id: 2, quantity: 3, price: 100, total: 300}, {id: 3, quantity: 5, price: 40, total: 200}, {id: 4, quantity: 2, price: 10, total: 20}];
const maxTotal = 150;
let new_arrays = {};
let index = 0;
let final_total = 0;
new_arrays['array_'+index] = [];
for (var obj of array) {
final_total += obj.total;
while (final_total > maxTotal) {
let exceed_value = final_total - maxTotal; // 350 - 150 = 200
let retain_quantity = parseInt((obj.total - exceed_value) / obj.price); // (300 - 200)/100 = 2
let remaining_quantity = obj.quantity - retain_quantity;
if (retain_quantity != 0) {
retain_obj = Object.assign({}, obj);
retain_obj.quantity = retain_quantity;
retain_obj.total = retain_quantity * obj.price;
new_arrays["array_"+index].push(retain_obj);
}
index++;
new_arrays['array_'+index] = [];
new_obj = Object.assign({}, obj, {quantity: remaining_quantity, total: remaining_quantity * obj.price})
new_arrays["array_"+index].push(new_obj);
// console.log(new_obj);
obj = new_obj;
final_total = remaining_quantity * obj.price;
new_arrays['array_'+index].pop(); // if loop is to repeat again
}
new_arrays["array_"+index].push(obj); // prints new and remaining quantities
}
console.log(new_arrays)
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)
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.
How do I retain the accumulative value of my reduce function? Each iteration resets the object value.
const a = [1, 2, 3, 4, 5];
const b = {
1: {
name: 'Dan',
age: 25
},
2: {
name: 'Peter',
age: 28
},
3: {
name: 'Mark',
age: 38
},
4: {
name: 'Larry',
age: 32
},
5: {
name: 'Simon',
age: 25
},
}
const f = a.reduce((acc, val) => {
console.log({
acc
})
return {
[val]: {
age: b[val].age
}
}
}, {})
console.log(f); // 5: {age: 25}
My desired outcome would be:
{
1: { age: 25 },
2: { age: 28 },
3: { age: 38 },
4: { age: 32 },
5: { age: 25 },
}
(This example is a demo)
Add the previous accumulator to the returned value using object spread (like this example) or Object.assign():
const a = [1, 2, 3, 4, 5];
const b = {"1":{"name":"Dan","age":25},"2":{"name":"Peter","age":28},"3":{"name":"Mark","age":38},"4":{"name":"Larry","age":32},"5":{"name":"Simon","age":25}};
const f = a.reduce((acc, val) => ({
...acc, // previous accumulator
[val]: {
age: b[val].age
}
}), {})
console.log(f); // 5: {age: 25}
As per MDN
The reduce() method applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.
On each iteration you return new object inside of reduce() function and you are not storing previous value of that accumulator. So you need to be merge or assign previous value with new value.
One way you can use Object.assign() method to get the required result.
DEMO
const a = [1, 2, 3, 4, 5];
const b = {1: {name: 'Dan',age: 25},2: {name: 'Peter',age: 28},3: {name: 'Mark',age: 38},4: {name: 'Larry',age: 32},5: {name: 'Simon',age: 25}}
let result = a.reduce((acc, val) => Object.assign(acc,{[val]: {age:b[val].age}}), {});
console.log(result);
.as-console-wrapper {max-height: 100% !important;top: 0;}
second way you can do like this obje[val]=newValue and return accumulator.
DEMO
const a = [1, 2, 3, 4, 5];
const b = {1: {name: 'Dan',age: 25},2: {name: 'Peter',age: 28},3: {name: 'Mark',age: 38},4: {name: 'Larry',age: 32},5: {name: 'Simon',age: 25}}
let result = a.reduce((acc, val) =>{
acc[val]= {age:b[val].age};
return acc;
}, {});
console.log(result);
.as-console-wrapper {max-height: 100% !important;top: 0;}
Another way you can combine using the spread syntax
DEMO
const a = [1, 2, 3, 4, 5];
const b = {1: {name: 'Dan',age: 25},2: {name: 'Peter',age: 28},3: {name: 'Mark',age: 38},4: {name: 'Larry',age: 32},5: {name: 'Simon',age: 25}}
let result = a.reduce((acc, val) => {
return {...acc,...{[val]:{age:b[val].age}}}
}, {});
console.log(result);
.as-console-wrapper {max-height: 100% !important;top: 0;}
Your reduce accumulator can also reset if you happen to have an if else statement in your reduce function, whereby you return an accumulator for the if statement but forget to handle the else statement and do not return an accumulator - in that case when the else statement is triggered no accumulator is returned and the accumulator becomes undefined.
For example this works:
let reducedArray = array.reduce((acc, curr) => {
if (
curr[
property_name
]
) {
return {
...acc,
...{
[curr.id]:
(acc[curr.id] ?? 0) +
curr[
property_name
]!
},
};
} else {
return acc;
}
}, {} as { [key: string]: number });
But this will not work:
let reducedArray = array.reduce((acc, curr) => {
if (
curr[
property_name
]
) {
return {
...acc,
...{
[curr.id]:
(acc[curr.id] ?? 0) +
curr[
property_name
]!
},
};
}
}, {} as { [key: string]: number });