javascriptarrayscallbackreducespread-syntax

array method(s) inaccessible with MyObject.prototype.reduce callback


So, I'm trying out some prototyping, and have had success implementing forEach on the prototype to handle its array of objects. The arrow function callback works fine, and I thought the same thing might work for .reduce(), however, as you can see, the notation which works for a normal Array does not work for my ArraySet prototype. On a side note, the same function in arrow notation does not work.

What's missing in my understanding of what's happening here so that I can fix this and move on?

function ArraySet(items) {
  // this._items = []
  this._items = items
}

ArraySet.prototype.forEach = function forEach(cb) {
   return this._items.forEach(cb);
}
  
ArraySet.prototype.reduce = function reduce(cb) {
  return this._items.reduce(cb);
}

let arr = new ArraySet([{
    key1: 'property2',
    key3: 'propertyx'
  },
  {
    key1: 'property4',
    key3: 'propertyy'
  },
  {
    key1: 'climate change',
    key3: 'propertyx'
  },
  {
    key1: 'climate change',
    key3: 'propertyx'
  },
])

arr.forEach(el => {
    console.log(el)
});

x = arr.reduce(function (map, obj) {
    if (obj.key3 === 'propertyx'){
        map.push(obj.key1)
    }
    return map
}, []) //<-- final argument is the instantiating literal of the reigning map type: [], {}, ''

EDIT: Thanks to Maheer Ali's answer detailing the use of the spread operator (...) the problem was easily solved. Maheer excellently expands on other functions where the same approach will apply.

Digging into the why, I learned before the spread operactor came along .apply() was customarily used in function calls to ensure all required arguments were available in the execution. The spread operator has evolved from applicability to arrays (like a list of arguments) since it was introduced to also include objects. It can also copy an array, replacing arr.splice().

Here's an adaptation of one of the examples on the MDN:

function myFunction(v, w, x, y, ...z) {
  console.log(v + ' ' + w + ' ' + x + ' ' + y + ' ' + z)
}
var args = [0, 1];
myFunction(-1, ...args, 2, ...[3, 8]);

More available in the reference material: Spread Syntax


Solution

  • The reduce() have two parameters one is the callback and second is initial value of accumulator. So you need to use rest parameters for the methods and then pass all parameters to reduce()

    Note: In reduce() you usually pass second argument. In forEach(),map() there is also a second optional parameter. That parameter will be this binded to callback passed to particular method.If you need to use that make sure to do the same as reduce()

    See the below working snippet

    function ArraySet(items) {
      // this._items = []
      this._items = items
    }
    
    ArraySet.prototype.forEach = function forEach(cb) {
       return this._items.forEach(cb);
    }
      
    ArraySet.prototype.reduce = function reduce(...args) {
      return this._items.reduce(...args);
    }
    
    let arr = new ArraySet([{
        key1: 'property2',
        key3: 'propertyx'
      },
      {
        key1: 'property4',
        key3: 'propertyy'
      },
      {
        key1: 'climate change',
        key3: 'propertyx'
      },
      {
        key1: 'climate change',
        key3: 'propertyx'
      },
    ])
    
    arr.forEach(el => {
        console.log(el)
    });
    
    x = arr.reduce((map, obj) => {
        if (obj.key3 === 'propertyx'){
            map.push(obj.key1)
        }
        return map
    }, []) //<-- final argument is the instantiating literal of the reigning map type: [], {}, ''
    
    console.log(x)

    On a side note, the same function in arrow notation does not work

    The arrow function doesn't have their own this bindings. They use this of parent scope. As you are are using this in your methods so you can't use Arrow functions. Prototype methods can't be ever written with arrow functions.