For future readers, there are a few more idiomatic ways to write the reduction in a functional way.
These are generally used because they convey intent a bit more cleanly (and don't add a variable to the scope).
Note: I am assuming this.items
has type
({ quantity: number; price: number } | undefined)[] | undefined
but each of the examples is tolerant to even more invalid data than the two in the question.
Filtering and mapping before reducing
Default value at the end
return this.items
?.filter(item => item?.quantity && item.price)
.map(item => item.quantity * item.price)
.reduce((a, b) => a + b, 0) ?? 0
Default array at the start
return (this.items ?? [])
.filter(item => item?.quantity && item.price)
.map(item => item.quantity * item.price)
.reduce((a, b) => a + b, 0)
Handling the filter within the map
I would not recommend these just because the previous two convey intention more clearly.
Default value at the end
return this.items
?.map(item => (item?.quantity ?? 0) * (item?.price ?? 0))
.reduce((a, b) => a + b, 0) ?? 0
Default array at the start
return (this.items ?? [])
.map(item => (item?.quantity ?? 0) * (item?.price ?? 0))
.reduce((a, b) => a + b, 0)
Destructuring
Each of the previous examples can be done with destructuring instead.
I am including one example.
return (this.items ?? [])
.filter(item => item) // Ensure item exists; sufficient for the cases we need to worry about
.map(({ price = 0, quantity = 0 }) => quantity * price)
.reduce((a, b) => a + b, 0)
Without a map
We can now do the reduction without a map.
This can also be done without destructuring, but that is seemingly (to me) inelegant.
return (this.items ?? [])
.filter(item => item)
.reduce((sum, { price = 0, quantity = 0 }) => sum + quantity * price, 0)
Of course, you can change the filter condition, which takes us back to roughly the first example in the question:
return (this.items ?? [])
.filter(item => item?.price && item.quantity)
.reduce((sum, { price, quantity }) => sum + quantity * price, 0)
Original forEach
loop
Some of these changes can be made to the original loop, too:
let total = 0;
items?.forEach((item) => {
if (item?.quantity && item.price) {
total += item.quantity * item.price;
}
});
return total;
forEach
isn't designed resp. implemented to be run in parallel, then it is not suited for distributed environments and the second solution will not work for parallel computations and thus the two solutions are completely different. – Babyblueeyes