I have an array of items like so:
@items = [
{price: 12, quantity:1},
{price: 4, quantity:1},
{price: 8, quantity:1}
]
And I am looking for something like this:
sumPrice: ->
@items.sum (item) -> item.price * item.quantity
Or anything as close as possible to this, that makes it super-easy for everyone reading the code to understand whats happening.
So far I came up with:
sumPrice: ->
(items.map (a) -> a.price * a.quantity).reduce (a, b) -> a + b
And:
sumPrice: ->
sum = 0
for item in items
sum += item.price * item.quantity
sum
I love CoffeeScript so I hope there is a nicer solution to this & similar scenarios that I miss.
Functional style is not so bad. CoffeeScript allows you to prettify your code like this:
items
.map (item) ->
item.price * item.quantity
.reduce (x,y) ->
x+y
This code is easier for understanding than your one-liner.
If you don't like map you can use for instead. Like this:
(for item in items
item.price * item.quantity)
.reduce (x,y)->x+y
Or like this:
prods = for item in items
item.price * item.quantity
prods.reduce (x,y)->x+y
Or you can add your own sum() method for arrays:
Array::sum = -> @reduce (x,y)->x+y
(item.price * item.quantity for item in items).sum()
If you want to express the solution as @items.sum (item) -> item.price * item.quantity you can add a sum method to Array:
Array::sum = (fn = (x) -> x) ->
@reduce ((a, b) -> a + fn b), 0
sum = @items.sum (item) -> item.price * item.quantity
Notice that i'm passing 0 as the initial value of reduce so the fn callback is called for every array value.
If you don't like extending the builtin objects, i guess you could express the sum as a single reduce elegantly if you extract the logic of calculating the total price for a single array item in its own function:
itemPrice = (item) -> item.price * item.quantity
sum = items.reduce ((total, item) -> total + itemPrice item), 0
You can use destructuring to simplify the code slightly:
sumPrice: ->
sum = 0
sum += price * quantity for {price, quantity} in @items
sum
I don't think there's any way to get rid of the explicit initialization of sum. While Coffeescript's for loop syntax tends to help simplify code that would otherwise use map(), it doesn't really have anything analogous that simplifies reduce()-type operations, which is what sumPrice is doing here.
As mentioned in the comments, one advantage this solution has over a call to reduce() or sum() is that it avoids the overhead of creating and repeatedly calling a function.
sum = 0
value = (item) ->
item.price * item.quantity
sum += value(item) for item in @items
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With