Элегантно делайте сумму атрибутов объекта в CoffeeScript - программирование

Элегантно делайте сумму атрибутов объекта в 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, поэтому я надеюсь, что есть лучшее решение для этого и подобных сценариев, которые я пропускаю.

4b9b3361

Ответ 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