Элегантно делайте сумму атрибутов объекта в CoffeeScript
У меня есть массив таких элементов:
@items = [
{price: 12, quantity:1},
{price: 4, quantity:1},
{price: 8, quantity:1}
]
И я ищу что-то вроде этого:
sumPrice: ->
@items.sum (item) -> item.price * item.quantity
Или все как можно ближе к этому, что делает его очень легким для всех, кто читает код, чтобы понять, что происходит.
До сих пор я придумал:
sumPrice: ->
(items.map (a) -> a.price * a.quantity).reduce (a, b) -> a + b
- содержит слишком много функциональной магии
- теряет описательность
и
sumPrice: ->
sum = 0
for item in items
sum += item.price * item.quantity
sum
- которые могут быть поняты начинающим программистам JS/Coffee
- чувствует себя немного глупо.
Мне нравится CoffeeScript, поэтому я надеюсь, что есть лучшее решение для этого и подобных сценариев, которые я пропускаю.
Ответы
Ответ 1
Если вы хотите выразить решение как @items.sum (item) -> item.price * item.quantity
, вы можете добавить метод sum
в Array
:
Array::sum = (fn = (x) -> x) ->
@reduce ((a, b) -> a + fn b), 0
sum = @items.sum (item) -> item.price * item.quantity
Обратите внимание, что я передаю 0
в качестве начального значения reduce
, поэтому обратный вызов fn
вызывается для каждого массива значение.
Если вам не нравится расширение встроенных объектов, я думаю, вы могли бы выразить сумму как единое сокращение элегантно, если вы извлекаете логику вычисления общей цены для одного элемента массива в своей собственной функции:
itemPrice = (item) -> item.price * item.quantity
sum = items.reduce ((total, item) -> total + itemPrice item), 0
Ответ 2
Функциональный стиль не так уж плох. CoffeeScript позволяет вам приукрасить свой код следующим образом:
items
.map (item) ->
item.price * item.quantity
.reduce (x,y) ->
x+y
Этот код легче понять, чем ваш однострочный.
Если вам не нравится map
, вы можете использовать for
. Вот так:
(for item in items
item.price * item.quantity)
.reduce (x,y)->x+y
Или вот так:
prods = for item in items
item.price * item.quantity
prods.reduce (x,y)->x+y
Или вы можете добавить свой собственный метод sum()
для массивов:
Array::sum = -> @reduce (x,y)->x+y
(item.price * item.quantity for item in items).sum()
Ответ 3
Вы можете использовать деструктуризацию, чтобы немного упростить код:
sumPrice: ->
sum = 0
sum += price * quantity for {price, quantity} in @items
sum
Я не думаю, что есть способ избавиться от явной инициализации sum
. Хотя синтаксис цикла Coffeescript for
имеет тенденцию помогать упрощать код, который в противном случае использовал бы map()
, на самом деле он не имеет ничего аналогичного, что упрощает операции reduce()
-type, что здесь делает sumPrice
.
Как упоминалось в комментариях, одно из преимуществ этого решения заключается в вызове reduce()
или sum()
заключается в том, что он позволяет избежать накладных расходов на создание и многократное вызов функции.
Ответ 4
sum = 0
value = (item) ->
item.price * item.quantity
sum += value(item) for item in @items