top | item 9315829

(no title)

ryanthejuggler | 11 years ago

The "official" way to do this is map, then reduce. The way reduce is meant to be used exactly matches this implementation.

Consider that you have a large quantity of numbers that you want to get the min, max, mean for. If you write something like the following:

    function minMaxMean(list) {
      return list.reduce(function(lastState, n) {
        var min = lastState.min,
            max = lastState.max,
            sum = lastState.sum,
            count = lastState.count;
        if(n < min) min = n;
        if(n > max) max = n;
        sum += n;
        count += 1;
        return {
          min: min,
          max: max,
          sum: sum,
          count: count
        }
      }, {min: Infinity, max: -Infinity, sum: 0, count: 0});
    }
...then you're assuming that the reduce function will run once, over a single list of numbers, in order from left to right. However, if you implement it as the following:

    function minMaxMean(list) {
      return list.map(function(n){
        return {
          min: n,
          max: n,
          sum: n,
          count: 1
        }
      }).reduce(function(a, b) {
        return {
          min: (a.min < b.min ? a.min : b.min),
          max: (a.max > b.max ? a.max : b.max),
          sum: a.sum + b.sum,
          count: a.count + b.count
        }
      });
    }
...then you can distribute this out across multiple threads/machines/etc, update it when new data comes in, reduce in any order.

discuss

order

espadrine|11 years ago

There are many cases of elegantly using reduce() with a different return type than the list's item type.

Here is a JS function which computes the combined length of all strings in a list:

    stringsLen = (strings) => strings.reduce((acc, item) => acc += item.length, 0);
    
    stringsLen(['hello', 'world'])  //> 10
Sure, you can argue that this works, but it misses the point.

    stringsLen = (strings) => strings.map(s => s.length).reduce((acc, n) => acc += n, 0);
    
     stringsLen(['hello', 'world'])