Search code examples
javascriptfunctional-programming

What is the big difference between forEach and map?


Here's something I don't understand. The map function, which I just learned about a few days ago, is supposed to be some amazing function that will transform the way I write code if I find uses for it. But I still don't see how it's any different than forEach. The only difference is that the function passed to map replaces the current element with the return value. But forEach can do that too, which means that map is just a less general version of forEach.

Example on MDN:

var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots is now [1, 2, 3], numbers is still [1, 4, 9]

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Ok, cool, I guess?

I can do that with forEach:

var numbers = [1, 4, 9];
var roots = numbers.forEach(function(el){el=Math.sqrt(el);});
// roots is now [1, 2, 3], numbers is still [1, 4, 9]

Other example:

var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}];
var reformattedArray = kvArray.map(function(obj){ 
   var rObj = {};
   rObj[obj.key] = obj.value;
   return rObj;
});
// reformattedArray is now [{1:10}, {2:20}, {3:30}], 
// kvArray is still [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}]

I can do that with forEach and it looks almost identical.

What's so good about map? What is an example where it works super-well where forEach wouldn't work?


Solution

  • The difference is that forEach "iterates over an array" and map "iterates over an array and returns the result"

    var numbers = [1, 4, 9];
    
    var roots = numbers.forEach(Math.sqrt);
    // roots is undefined, numbers is still [1, 4, 9]
    
    var roots = numbers.map(Math.sqrt);
    // roots is [1, 2, 3], numbers is still [1, 4, 9]
    

    What's so good about map? It keeps your code small and smart because you don't need to define anonymous functions for built-in functions.

    var numbers = [1, 4, 9];
    
    var roots = [];
    numbers.forEach(function(val){ roots.push( Math.sqrt(val) ); });
    // roots is now [1, 2, 3], numbers is still [1, 4, 9]
    

    The value of returning the result (no pun intended) is proven in your existing example...

    var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}];
    var reformattedArray = kvArray.map(function(obj){ // <---------------- map
       var rObj = {};
       rObj[obj.key] = obj.value;
       return rObj;
    });
    // reformattedArray is now [{1:10}, {2:20}, {3:30}], 
    // kvArray is still [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}]
    
    var kvArray = [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}];
    var reformattedArray = kvArray.forEach(function(obj){ // <---------------- forEach
       var rObj = {};
       rObj[obj.key] = obj.value;
       return rObj;
    });
    // reformattedArray is now undefined, 
    // kvArray is still [{key:1, value:10}, {key:2, value:20}, {key:3, value: 30}]