Frage

I have an object wherein I am storing selections of a shopping cart. Because this is a Backbone app I decided that to derive the subtotal of the cart items I would use reduce to add the item prices by quantities:

Here's a (contrived) example:

var App = {
    cart : {
        item : []
    }
}
//...
App.cart.item.push({ qty: 1, price: 34.95});
App.cart.item.push({ qty: 3, price: 24.95});
App.cart.item.push({ qty: 4, price: 1.99});
App.cart.item.push({ qty: 13, price: .99});

When the view is rendered the subtotal is calculated as follows:

_.reduce(App.cart.item,function(memo, num){ return memo + (num.price * num.qty); },0)

This sparked some debate:

  • Some colleagues have a disagreement that reduce is not the correct method to use here, rather, to use each and pass to a summing function and possibly use a memoize pattern to cache the result.
  • Another argues that I shouldn't use reduce without passing the function output from a matching map.
  • Still others agree that while reduce is the correct method to use I should instead use the foldl alias as it's clearer to future devs what the intention is.

Admittedly I know very little about FP, so my use of the reduce method in Underscore is purely a means to an end of deriving a subtotal without having to store it as a separate property in the JSON server-side.

Please explain to me why reduce is or is not "correct" approach to produce sums of the object properties. I'm not looking for a pure functional approach here, obviously, as I'm only using reduce as a utility.

War es hilfreich?

Lösung

This is a pretty subjective question, so I suppose I'll give a subjective answer:

  • There are basically three things at issue here: actual code efficiency, code legibility, and code style. The last one doesn't really matter IMO except insofar as it affects legibility and the consistency of your code base.

  • In terms of efficiency, _.reduce is more efficient than _.map + _.reduce, so you can drop that idea. Using _.map creates a new array with the transformed values - there's no need for this.

  • The _.each + summation function approach may be marginally less efficient (you need to keep track of your running-total variable somewhere else) and to my mind it's less legible, because it takes the relevant code and moves it further from the loop where you're using it. You also don't get a nice clean return value for your operation - instead, you have a variable hanging out somewhere in the outer scope that you need to first create and then re-use.

  • I don't think "memoization" makes much sense here. You might want to cache the return value for a given cart, and invalidate that on item changes, but that's not actually memoizing. Memoization makes sense when a) an easily-hashed input will always produce the same answer, and b) calculating that answer is expensive. In this case, calculating the sum is probably cheaper than hashing the input (your item list).

  • In terms of legibility, I tend strongly towards using the Underscore aliases that shadow the names of real JS 1.8 methods (the exception, for me, is .any vs. .some, because I find it so much more legible). In this case, that means .reduce, not .inject or .foldl. That makes it easier for a JS dev to understand what's intended, and JS devs are who you care about.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top