Reduce() in depth

In ES5, the new array method reduce(). I am wondering if someone can explain more in depth.

var test = [1, 2, 3, 4, 5].reduce(function(inital, item, idx) {
  return inital + item
}, 0);

console.log(test);

In this example, we know for a fact that initial argument is 0 and loops through with callback function. Can someone explain it to me the scopes of initial argument?

If we assume that [1, 2, 3, 4, 5] is what is returned from the imaginary method and want to return an object form which key is idx and value is Math.pow(idx,2). {1:1, 2:4, 3:9, 4:16, 5:25}. Can someone please explain it to me?

ALSO :) we have an array with lettters. some repeats some don't. I want to count how many letters are are in an array and turn it into the object form. {a:5, .... z:7}; Someone please walk me through with it. I understand forEach and map just fine, but with the reduce method, I am having very difficult time getting my head around it.

thank you in advance

Answers:

Answer

Imagine we have the following code snippet somewhere in our application:

function addLog() {
  console.log(arguments)
  return arguments[0] + arguments[1]
}

console.log([2,3,4].reduce(addLog))
console.log([2,3,4].reduce(addLog, 1))

Unfortunately we have a dependency on a library called badlibrary.js which will overwrite ES5's default Array.prototype.reduce:

badlibrary.js:

Array.prototype.reduce = Infinity

Since this library is a dependency for our application, we cannot remove the library. The only solution is to fix Array.prototype.reduce by rewriting it before we run our code.

It turns out that this is not so difficult. We can just read Mozilla's documentation on reduce and translate the English to JavaScript.

Array.prototype.reduce = function(callback, initialValue) {
  var resultSoFar, i
  var n = this.length
  if (typeof initialValue !== 'undefined') {
    resultSoFar = initialValue
    i = 0
  } else {
    resultSoFar = this[0]
    i = 1
  }
  for (; i < n; i++) {
    resultSoFar = callback(resultSoFar, this[i], i, this)
  }
  return resultSoFar
}

Our code now works, even with this custom version of reduce, and now reduce is no longer a "black box". We could easily write it ourselves if we wanted to, and in fact, that is what several people did before ES5 was standardized. By the way, some people still use browsers that only support up to ES3. For reduce to be truly cross-browser compatible, we still need to shim it or use a utility library like Underscore/Lodash/Ramda. Using a utility library is IMO, the best and easiest solution.

ALSO :) we have an array with lettters. some repeats some don't. I want to count how many letters are are in an array and turn it into the object form. {a:5, .... z:7}; Someone please walk me through with it. I understand forEach and map just fine, but with the reduce method, I am having very difficult time getting my head around it.

The thing we want to return is a JS object with the letters as the keys and their counts as the values. We called this value resultSoFar in our reduce function and we are going to build that value up by calling our function on each of the elements of the array. Our initial value should be an empty object. In our function, we'll take the current letter and try to add 1 to its count. If this produces NaN (which will happen if the count is undefined i.e. when we are seeing a letter for the first time and no count has been assigned yet), then we should assign the value to 1 instead since its a new letter to count and we're seeing one of them.

With this theory in mind, the counting code can be written as follows:

var letters = ['a', 'b', 'c', 'a', 'b', 'a']
console.log(letters.reduce(function(counts, currentLetter) {
    var currentLetterCount = counts[currentLetter]
    counts[currentLetter] = currentLetterCount + 1 || 1
    return counts
  }, {}))
Answer

The method you pass into .reduce takes parameters previous_returned_value, current_iteration_item, current_iteration_index, context_array

This means that each time it is called, the first argument is the return value from the previous invocation

The initial value is what is passed in only the very first time.

You can read the MDN docs about it here


return an object form which key is idx and value is Math.pow(idx,2)

  • You pass in an initial empty object
  • Your method adds properties to this object
  • Method returns the object

e.g.

 [1, 2, 3, 4, 5].reduce((o, e) => (o[e] = Math.pow(e, 2), o), {});
 // Object {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

count how many letters are are in an array and turn it into the object

  • Again pass in an initial empty object
  • Method sets/adds 1 to the object based on the current letter
  • Method returns the object

So again

['f', 'o', 'o', 'b', 'a', 'r'].reduce((o, e) => (o[e] = (o[e] || 0) + 1, o), {});
// Object {f: 1, o: 2, b: 1, a: 1, r: 1}

Please note the arrow notation functions I've used exploit the comma operator to return what I want, more traditionally you might write the function as e.g. followed by evolution to my examples

function (o, e) {
    o[e] = Math.pow(e, 2);
    return o;
}
// same as
function (o, e) {
    return o[e] = Math.pow(e, 2), o;
}
// similar to
(o, e) => {
    return o[e] = Math.pow(e, 2), o;
}
// same as
(o, e) => (o[e] = Math.pow(e, 2), o);
Answer

The reduce() method is an iterator it loops through all the elements in an array and does something with the results. The reduce() function accepts a callback function. This the function that actually executes whatever you want it to do for every iteration. This function is a special function

the first parameter this function takes is previous value the second parameter this function takes is current value and two other parameters (let just make this simple)

lets consider we have an array

arr=[1,2,3,4,5,6]

using reduce by creating a function called sum

function sum(previousvalue,currentvalue,index,ar){
return previousvalue+currentvalue
}

and finally we place a reference of this function in the reduce method

arr.reduce(sum)

executing this we get 21

What really is this special function doing? let's consider

[1,2,3,4,5,6]

function sum says

Wrong! "let me take the current element value in this array " == 1


Right! # the function starts at the second element in the array so the previous element is the 1st element

 "let me add the current element value to the prev. element value in this array and remember this value " == 1+2


"okay I am moving on to the next value"==3

"okay..I will add the next value to the previous summed up value"==3+3
"now I will save this value and do the same thing again until I loop to the end"

But wait what is the practical use of this, I can write this easily without using the reduce function?

It is like a generator function (like in some languages such as python)..it doesn't remember values except the previous call value and the current value...(some memory efficiency advantage)

example finding moving averages

   function sum(previousvalue,currentvalue,index,ar){
    return (previousvalue+currentvalue/2)
    }

the function above is the revision of a the same old funciton i was using

-It takes the first two element find the average and save the value -It takes the next element add it the average and find another average and the cycle goes on

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us Javascript

©2020 All rights reserved.