简体   繁体   中英

Several small reduce vs one big forEach?

Using reduce :

addSales(sales: Sale[]) {
  this.total += sales.reduce((ac, sale) => ac + sale.total, 0);
  this.tax += sales.reduce((ac, sale) => ac + sale.tax, 0);
  this.discount += sales.reduce((ac, sale) => ac + sale.discount, 0);
  this.qty += sales.reduce((ac, sale) => ac + sale.qty, 0);
}

Using .forEach

addSales(sales: Sale[]) {
  sales.forEach(sale => {
    this.total += sale.total;
    this.tax += sale.tax;
    this.discount += sale.discount;
    this.qty += sale.qty;
  });
}

Using .forEach here seems to be more readable, and perhaps better in performance since it will only loop through it once.

But in the long run, does it make it less modular? I have some parts of my code in which started with small .forEach block, but overtime it grew in size and was somehow difficult to refactor, since they were "tied" to each other.

Could you share your experience on this?

Richard's answer is good but if you want to stick with reduce to avoid the albeit localized mutation you can do so while still visiting each object only once. In fact the beauty of reduce is that we can transform an array into any arbitrary value that we want, so let's have fun with it

export function tallySales(sales: Sale[]) {
  return sales.reduce((ac, {total, tax, discount, qty}) => ({
    total: ac.total + total,
    tax: ac.tax + tax, 
    discount: ac.discount + discount,      
    qty: ac.qty + qty
  }), {total: 0, tax: 0, discount: 0, qty: 0});

With respect to the specific the question about modularity, using the above style is by no means more modular, but it does encourage us to write more testable code as we express things in terms of inputs and outputs to functions. The function above can be tested very easily, while the imperative version that mutates the class instance's dynamic binding is more complicated to test although not necessarily by much in this case.

Interestingly, it's not really about using reduce versus forEach at all. In fact the above function could be written with forEach and it would be as maintainable and testable and as modular as it is with reduce . It has more to do with how using reduce gets us in the mind frame of expressing our interfaces in terms of values instead of modifying state that is associated with many producers and consumers to implicitly move the program forward.

There's nothing wrong with using forEach here, and it's certainly more efficient than doing multiple calls to reduce (albeit to a negligible degree unless sales can contain a very large number if values). It's also more readable, as you mentioned

You could pass this to reduce as the seed and do all the changes together, but since the code is mutating the instance there's not much to be gained by using a more functional approach.

I'd stick with forEach.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM