0

I have defined my own map function, which serves the same purpose as the method on Array objects. (I am doing this to learn). The code is below.

I would like to use Array's forEach method inside of my own definition of map, but I am missing something conceptually. How would I be able to do this using forEach?

Working without using forEach

// transforming with map
// NOTE: map is already defined in JavaScript
function map(array, transformation) {
  var mapped = [];

  for (var index in array) {
    var currentElement = array[index];
    var transformedElement = transformation(currentElement);
    mapped.push(transformedElement);
  }

  return mapped;
}

My Attempt of using forEach

function mapForEach(array, transformation) {
    var mapped = [];

    array.forEach(transformation(currentVal, index, array, mapped));

    return mapped;
}

function transformation(person, index, array, accumulator) {
    var name = person.name;

    accumulator.push(name);
}
Steven L.
  • 2,045
  • 3
  • 18
  • 23
  • `array.forEach(transformation(currentVal, index, array, mapped));` - You are *calling* transformation here, and passing its return value (none) into forEach. You might need to use Function.bind if you want to access `mapped` inside the loop without using a closure. – Katana314 Aug 20 '15 at 15:29

2 Answers2

0

The missing concept here is pass by value. The changes to accumulator will not be reflected in forEach. forEach is not actually designed to return a value for each iteration. For that you have map.

Taken from here,

foreach iterates over a list and applies some operation with side effects to each list member (such as saving each one to the database for example)

map iterates over a list, transforms each member of that list, and returns another list of the same size with the transformed members (such as converting a list of strings to uppercase)

Try this code:

function mapForEach(array, transformation) {
    var mapped = array.map(transformation);
    return mapped; 
}

function transformation(person, index, array) {
    var name = person.name;
    return name; 
}

Here is a JSFiddle

If you absolutely have to use forEach, then rather than passing a value, a global variable has to be used. This would be then

var accumulator =[];

function mapForEach(array, transformation) {
    array.forEach(transformation);
    return accumulator;
}

function transformation(person, index, array) {
    var name = person.name;
    accumulator.push(name);
}

JSFiddle - forEach

Community
  • 1
  • 1
aarjithn
  • 1,151
  • 8
  • 20
0

You're almost right with your method, but like @Katana314 says, you need to bind, not call.
Here's how I would do it:

function map(array,mapper) {
    var ret=[];
    array.forEach((val,key)=>{
        //Since arrays are supposed to be sequential, we don't have to worry
        //about keys matching, if they don't match, the user/developer did
        //something horribly wrong.
        ret.push(mapper(val,key,array));
    });
    return ret;
}

And here's how I would fix your way.

function mapForEach(array, func) {
    var mapped = [];
    array.forEach(mapForEachPusher.bind(null,mapped,func));
    return mapped;
}
function mapForEachPusher(mapped,func,value,index,array) {
    mapped.push(func(value,index,array));
}

function extract_name(person) {
    return person.name;
}
mapForEach(
    [{name:'Billy'},{name:'Bob'},{name:'Bridget'},{name:'Brittany'},{name:'"B word"'}]
    ,extract_name
);
//["Billy", "Bob", "Bridget", "Brittany", "\"B word\""]

It's effectively the same thing, your way moves the .push() call out of the main function, which means you have to pass data around, whereas I only need to reference from the outer scope. The only downside to my way is the overhead of the function, but it is an arrow function, so that might make it lightweight enough to not matter.

Chinoto Vokro
  • 928
  • 8
  • 17