Array.prototype.reduce

JavaScript polyfill, by , Monday, April 21st, 2014

If you need to accumulate a value based on an array, the Array.prototype.reduce method is very convenient, but if an important part of your users use older browsers that do not support the feature, you can use one of the polyfills from this article. I will present a few different approaches, each with an explanation of how they work.

Reduce polyfill using for-loop

A very simple way to write this polyfill is using a normal for-loop. In this example, I have not included throwing errors, but if you wish your code to do that, you can take the throw conditions from the last example in this article, and place them where needed in your code. If you prefer, you can easily rewrite this version to use a while-loop. Practically it makes no difference, I just happened to pick a for-loop, but it is entirely up to your preference which you choose.

  1. if (!Array.prototype.reduce) {
  2. Array.prototype.reduce = function(callbackfn, initVal) {
  3. "use strict";
  4. var arr = this,
  5. arrLen = arr.length,
  6. k = 0,
  7. accumulator = initVal === undefined ? undefined : initVal;
  8.  
  9. for(;k < arrLen;k++) {
  10. if (accumulator !== undefined && k in arr) {
  11. accumulator = callbackfn.call(undefined, accumulator, arr[k], k, arr);
  12. } else {
  13. accumulator = arr[k];
  14. }
  15. }
  16. return accumulator;
  17. };
  18. }

The way this code works is, that the accumulator is the accumulated value returned at the end of the reduce operation. That is, if you have the array; [0, 1, 2, 3] and your callback function adds the the current value to the previous, then the accumulator at the end of the operation would be equal to 6. Of course the accumulator can be any type you want, like another array or an object, but no matter what, this is what the accumulator variable represents. At line 7 we set the initial value of the accumulator, if one is provided through the initVal parameter. If nothing is provided the accumulator gets initialized to undefined. From line 9-15 we iterate over the passed array. At line 10 we check that the accumulator is not undefined and that the current index being checked, is in fact in the passed array. If so, we assign the result of calling the provided callback (callbackfn) with the four parameters, accumulator, arr[k] the current value in the array, k the current index, and arr the array being iterated. If on the other hand this is not the case, we know that the accumulator is not defined yet, so we set it equal to the current value of the array, in line 13. Finally at line 16 we return the accumulated value of the calculation. As mentioned above the code, this version has no safety nets in the code, no errors are thrown even you feed it invalid input, so if that is something you would like, the next example will give you the code snippets you need, and you can feel free to move them to this code for use on your site.

Array.prototype.reduce ECMAScript 5.1 100% compliant polyfill

While the previous polyfill will work just fine for most uses, it is a good idea to look at an example that follows the specifications to the dot. First I will show you the code, and then following it, I will go through each step as specified in the ECMAScript 5.1 documentation section 15.4.4.21. So lets look at the code;

  1. if (!Array.prototype.reduce) {
  2. Array.prototype.reduce = function(callbackfn, initialValue) {
  3. "use strict";
  4. var O = Object(this),
  5. lenValue = O.length,
  6. len = lenValue >>> 0,
  7. k,
  8. accumulator,
  9. kPresent,
  10. Pk,
  11. kValue;
  12. if (typeof callbackfn !== 'function') {
  13. throw new TypeError();
  14. }
  15. if (len === 0 && initialValue === undefined) {
  16. throw new TypeError();
  17. }
  18. k = 0;
  19. if (initialValue !== undefined) {
  20. accumulator = initialValue;
  21. } else {
  22. kPresent = false;
  23. while(!kPresent && k < len) {
  24. Pk = k.toString();
  25. kPresent = O.hasOwnProperty(Pk);
  26. if (kPresent) {
  27. accumulator = O[Pk];
  28. }
  29. k += 1;
  30. }
  31. if (!kPresent) {
  32. throw new TypeError();
  33. }
  34. }
  35. while(k < len) {
  36. Pk = k.toString();
  37. kPresent = O.hasOwnProperty(Pk);
  38. if (kPresent) {
  39. kValue = O[Pk];
  40. accumulator = callbackfn.call(undefined, accumulator, kValue, k, O);
  41. }
  42. k += 1;
  43. }
  44. return accumulator;
  45. };
  46. }

This is obviously a lot more wordy than the previous example, and maybe not as obvious unless you have a look at what each line represents, so let do that.

The first three lines are not in the documentation, but never the less necessary to make the polyfill work. What they do is at line 1 checking if the Array.prototype.reduce method is available in the browser, and if not at line 2 declare and initialize it to be equal to a function that that takes two parameters;

  1. callbackfn; a function that will be called on each element in the array on which the reduce method is called. The callback will be called with the four parameters;
    • accumulator; the accumulation of the operations so far, which can be any type value such as a number, and object, or something else.
    • kValue; the current value at the current index of the array object.
    • k; the current index.
    • O; the object/array being reduced.
  2. initialValue; the value that will be the starting value for the accumulation. If left out, the first ownProperty of the object/array (O) will be used instead.

At line 3 we simply declare that the scope should be executed in strict mode, which simply put, is a cleaner better behaved and more predictable subset of the of JavaScript. With this said, lets look at the implementation step by step, as defined in the documentation, starting with line 4;

1. Let O be the result of calling ToObject passing the this value as the argument.

  1. var O = Object(this),

2. Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length".

  1. lenValue = O.length,

3. Let len be ToUint32(lenValue). (what we are doing here is a little bit of a hack. Since there are no unsigned integers in JavaScript, we use bitwise operators to achieve something equivalent. If you are interested, we are making a zero-fill-right-shift about which and other bitwise operators, you can read more at Mozillas bitwise operators documentation)

  1. len = lenValue >>> 0,

The lines from 7-11 are simply declaration of variables for later use.

4. If IsCallable(callbackfn) is false, throw a TypeError exception. (the only way we can check this, at the front end, is by checking if the provided callback is a function, so that is what we do)

  1. if (typeof callbackfn !== 'function') {
  2. throw new TypeError();
  3. }

5. If len is 0 and initialValue is not present, throw a TypeError exception.

  1. if (len === 0 && initialValue === undefined) {
  2. throw new TypeError();
  3. }

6. Let k be 0.

  1. k = 0;

7. If initialValue is present, then
7a. Set accumulator to initialValue. (here we make use of unused parameters being set to undefined)

  1. if (initialValue !== undefined) {
  2. accumulator = initialValue;

8. Else, initialValue is not present
8a. Let kPresent be false.

  1. } else {
  2. kPresent = false;

8b. Repeat, while kPresent is false and k < len.

  1. while(!kPresent && k < len) {

8b i. Let Pk be ToString(k).
8b ii. Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.

  1. Pk = k.toString();
  2. kPresent = O.hasOwnProperty(Pk);

8b iii. If kPresent is true, then
8b iii 1. Let accumulator be the result of calling the [[Get]] internal method of O with argument Pk.

  1. if (kPresent) {
  2. accumulator = O[Pk];
  3. }

8b iv. Increase k by 1.

  1. k += 1;

8c. If kPresent is false, throw a TypeError exception.

  1. if (!kPresent) {
  2. throw new TypeError();
  3. }

9. Repeat, while k < len

  1. while(k < len) {

9a. Let Pk be ToString(k).
9b. Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.

  1. Pk = k.toString();
  2. kPresent = O.hasOwnProperty(Pk);

9c. If kPresent is true, then
9c i. Let kValue be the result of calling the [[Get internal method of O with argument Pk.
9c ii. Let accumulator be the result of calling the [[Call]] internal method of callbackfn with undefined as the this value and arguments list containing accumulator, kValue, k, and O.

  1. if (kPresent) {
  2. kValue = O[Pk];
  3. accumulator = callbackfn.call(undefined, accumulator, kValue, k, O);
  4. }

9d. Increase k by 1.

  1. k += 1;

10. Return accumulator.

  1. return accumulator;

Maybe this helped you out in some way, which would make me happy, so feel free to use this code in any way you want, though I would appreciate a reference if you choose to post it on another website.