2012-12-19 18 views
14

Ho un array di oggetti in questo modo:Elegantemente fare una somma di oggetti attributi a CoffeeScript

@items = [ 
    {price: 12, quantity:1}, 
    {price: 4, quantity:1}, 
    {price: 8, quantity:1} 
] 

E io sto cercando qualcosa di simile:

sumPrice: -> 
    @items.sum (item) -> item.price * item.quantity 

O qualcosa più vicino possibile per questo, è estremamente facile per tutti leggere il codice per capire che cosa sta succedendo.

Finora mi si avvicinò con:

sumPrice: -> 
    (items.map (a) -> a.price * a.quantity).reduce (a, b) -> a + b 
  • contiene troppo funzionale magia
  • perde descrittivo

E:

sumPrice: -> 
    sum = 0 
    for item in items 
    sum += item.price * item.quantity 
    sum 
  • che può essere compreso da novizio programmatori JS/caffè
  • si sente un po 'stupido

amo CoffeeScript quindi spero che ci sia una soluzione più bello per questo & scenari simili che mi mancano.

risposta

11

Se si vuole esprimere la soluzione come @items.sum (item) -> item.price * item.quantity si può aggiungi un metodo sum a Array:

Array::sum = (fn = (x) -> x) -> 
    @reduce ((a, b) -> a + fn b), 0 

sum = @items.sum (item) -> item.price * item.quantity 

Si noti che sto passando 0 come valore iniziale di reduce in modo che venga richiamata la richiamata fn per ogni valore di matrice.


Se non ti piace estendere gli oggetti incorporati, penso che si potrebbe esprimere la somma come un singolo ridurre elegantemente se si estrae la logica di calcolo del prezzo totale per un singolo elemento dell'array nella sua propria funzione:

+0

Grazie per l'implementazione della somma! – hakunin

+1

@hakunin Prego. A proposito, ho dimenticato di menzionare che 'sum' ha la funzione identity come parametro predefinito in modo da poter sommare facilmente un elenco di numeri' [3, -4, 5] .sum() ':) – epidemian

+0

Mi piace la" funzione denominata " "approccio, rende il codice molto più pulito e autodocumentante di una grande pila di callback. Potrei fare un ulteriore passo avanti e aggiungerne un altro in modo che potessi 'sumPrices = (t, i) -> t + itemPrice (i); sum = items.reduce (sumPrices, 0) '. –

7

È possibile utilizzare destrutturazione per semplificare un po 'il codice:

sumPrice: -> 
    sum = 0 
    sum += price * quantity for {price, quantity} in @items 
    sum 

non credo che ci sia alcun modo per sbarazzarsi di l'inizializzazione esplicita di sum. Mentre la sintassi del ciclo for di Coffeescript tende a semplificare il codice che altrimenti utilizzerebbe map(), in realtà non ha nulla di analogo che semplifica le operazioni di tipo reduce(), che è ciò che sta facendo sumPrice.

Come accennato nei commenti, un vantaggio che questa soluzione ha su una chiamata a reduce() o sum() è che evita il sovraccarico di creare e chiamare ripetutamente una funzione.

+3

Oltre alla leggibilità, un altro motivo per cui credo che questa risposta sia superiour a quelli funzionali è che questo compila in un ciclo for semplice che è più veloce e migliore in memoria rispetto agli approcci funzionali. Potrebbe non avere importanza per la tua situazione, ma personalmente la trovo ancora più leggibile, per me è il meglio di entrambi i mondi. –

2
sum = 0 
value = (item) -> 
    item.price * item.quantity 
sum += value(item) for item in @items 
12

Lo stile funzionale non è poi così male. CoffeeScript consente di prettify il codice in questo modo:

items 
    .map (item) -> 
    item.price * item.quantity 
    .reduce (x,y) -> 
    x+y 

Questo codice è più facile per la comprensione di quanto il tuo one-liner.

Se non ti piace map invece puoi usare for.Come questo:

(for item in items 
    item.price * item.quantity) 
    .reduce (x,y)->x+y 

O come questa:

prods = for item in items 
    item.price * item.quantity 
prods.reduce (x,y)->x+y 

Oppure è possibile aggiungere il proprio metodo di sum() per gli array:

Array::sum = -> @reduce (x,y)->x+y 
(item.price * item.quantity for item in items).sum() 
+0

La rientranza funzionale sembra buona. Penso che sto mantenendo quello stupido però. Se nessuno arriva con un modo più carino di scriverlo fino a domani, accetto la tua risposta, grazie! – hakunin

+0

È TUTTO L'ultimo ... '[1,2,3,4] .sum() ## -> 10' –

Problemi correlati