JavaScript Jems - Functional Iteration
Written by Mike James   
Monday, 08 June 2020
Article Index
JavaScript Jems - Functional Iteration
Reduce

Reduce

The second most well known function of this kind is probably reduce. This doesn't just take each element in turn and modify it, it also keeps a running sum. The reduce method really does reduce an array to a single result. The function that you pass to it has four parameters – the accumulator for the running sum, the current element, the index and the entire array. You also have to optionally pass an initial value for the accumulator and although optional, in practice it is usually essential. For example, to sum the elements of an Array you would use:

const myArray = [1, 2, 3, 4, 5, 6];
const sum = myArray.reduce(function (acc, element) { 
                              return element + acc; 
                           }
                          ,0);
console.log(sum);

The initial value of the accumulator is set to zero and then each element is added to it in turn.

The call to reduce is equivalent to:

let acc=0;
for(const element of myArray){
     acc = element + acc; 
}

Notice that reduce in its simplest form is different from the other array methods that "hide" a for loop because it really does reduce an array to a single value. However, it doesn't have to. The power of "everything is an object" means that reduce can be used in ways that don't reduce anything.

For example:

const myArray = [1, 2, 3, 4, 5, 6]; 
const mycopy = myArray.reduce(function (acc, element) { 
                                acc.push(element);
                                return acc;
                               } ,[]); 
console.log(mycopy); 

Notice that the accumulator, acc, is initialized to a null array. This means that you can use it as an array in the update function and, for example, call its push method which adds the element to the end of the array. The result is a complete copy of the array rather than anything reduced.

You can see that it is possible to use reduce to do any of the jobs that map can do. Once you allow everything to be an object you do open up this sort of possibility.

A very common idiom is to chain this sort of method together in a fluent style. For example:

function square(x){ return x*x; } 
function sum(x,y){ return x+y; } 
const myArray = [1, 2, 3, 4, 5, 6]; console.log(myArray.map(square).reduce(sum));

computes the sum of squares of myArray. Notice, however, that this only works because map returns an Array that you can call reduce on and this involves two complete passes through the Array whereas an explicit for loop, or even forEach, would have done the job in one pass. This is inefficient and when you add it to the fact that map and reduce are generally slower than for loops, you can see that there is a cost to be paid for the clarity of expression.

One of the reasons for map/reduce to be a preferred way of writing advanced code is that they lend themselves to being computed on a parallel computer. The map can be split up between different processors each working on a different part of the array and the reduce can be computed in the same way, but with the different processors passing their results to a single collecting processor. This is the basis of parallel systems such as Hadoop, but there are no such advantages for JavaScript.

There are lots of similar array methods and you can look them up in the documentation, but here is a list and brief description accurate at the time of writing:

every(test(element))

tests every element in turn and returns true if they all test true

filter(test(element))

tests every element in turn and returns a new array with elements that test true

find(test(element))

returns the first element for which test is true

findIndex(element))

returns the index of the first element for which the test is true

flat(depth)

returns a new array with nested arrays flattened to the specified depth

flatMap(function(element),
                                  depth)

same as map(function(element)).flat(depth)

forEach(function(element))

apply the function to each element and only modify array if the function does.

includes(value)

true if value is an element

indexOf(value)

returns the first index of the value if in array or -1 otherwise

join()

concatenates each element as a string

lastIndexOf(value)

returns the last index of the value if in array or -1 otherwise

map(function(element))

returns a new array resulting from applying function to each element

reduce(function(element,acc),
                                       init)

apply function to each element setting acc to init and passing it to all calls to function

reduceRight(function,initial)

as for reduce but applied from right to left, i.e. high index values first

reverse()

reverse the values in the array in place - mutating

slice(begin,end)

return array from begin to end

some(test)

returns true if an element of the array makes test true

sort()

sorts array in place - mutating

There are also methods that involve partial scanning of the array, but these are the classic "hide the for loop" methods.

In remander of the chapter but not included in this extract:

  • Map And Set
  • Enumerable Versus Iterable
  • Generators and Iterables
  • Nearly Everything Is An Array
  • Functional Strings
  • Function-Oriented Programming

 

Now available as a book from your local Amazon.

JavaScript Jems:
The Amazing Parts

kindlecover

Contents

<ASIN:1871962579>

<ASIN:1871962560>

<ASIN:1871962501>

<ASIN:1871962528>

kotlin book

 

Comments




or email your comment to: comments@i-programmer.info

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

 



Last Updated ( Monday, 08 June 2020 )