What is really the difference between underscore _.each and _.map?

I'm using UnderscoreJs. Consider this code:

var docs = [
    {name : 'Anders', niche : 'Web Development'}, 
    {name : 'Johnny', niche : 'Design'}, 
    {name : 'Eric', niche : 'PhotoShop'}
];

var newDocs = _.map(docs, function (doc){
    delete doc.niche;
    return doc;
});

I doesn't matter if I use .each or .map here. The outcome is exactly the same.

What is really the difference between the two in the case above?

map is intended to be a functional mapping method: its function argument should return a value, but is not expected to have any side-effects.

each is just a functional replacement for an imperative for loop: its purpose is to have an effect, and it is not expected to return any value.

For example, this would be a more appropriate use for map:

var docs = getDocs();
var docTitles = _.map(docs, function (doc){
    return doc.title;
});
// expect `docs` to be unchanged

Whereas this would be an appropriate use for each:

var docs = getDocs();
_.each(docs, function (doc){
    delete doc.niche;
});
// expect `docs` to be altered.

_.each(list, iteratee)

Iterates over a list of elements, yielding each in turn to an iteratee function.

Each invocation of iteratee is called with three arguments: (element, index, list). If list is a JavaScript object, iteratee's arguments will be (value, key, list). Returns the list for chaining.

_.each({one: 1, two: 2, three: 3}, alert);
=> alerts each number value in turn...

_.map(list, iteratee)

Produces a new array of values by mapping each value in list through a transformation function (iteratee).

If list is a JavaScript object, iteratee's arguments will be (value, key, list).

_.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
=> [3, 6, 9]

see documentation

You can just look at the source code to see the difference:

  • _.each:

    _.each = _.forEach = function(obj, iteratee, context) {
        if (obj == null) return obj;
        iteratee = createCallback(iteratee, context);
        var i, length = obj.length;
        if (length === +length) {
          for (i = 0; i < length; i++) {
            iteratee(obj[i], i, obj);
          }
        } else {
          var keys = _.keys(obj);
          for (i = 0, length = keys.length; i < length; i++) {
            iteratee(obj[keys[i]], keys[i], obj);
          }
        }
        return obj;
    };
    
  • _.map:

    _.map = _.collect = function(obj, iteratee, context) {
        if (obj == null) return [];
        iteratee = _.iteratee(iteratee, context);
        var keys = obj.length !== +obj.length && _.keys(obj),
            length = (keys || obj).length,
            results = Array(length),
            currentKey;
        for (var index = 0; index < length; index++) {
          currentKey = keys ? keys[index] : index;
          results[index] = iteratee(obj[currentKey], currentKey, obj);
        }
        return results;
    };
    

Your assertion that the results are "exactly the same" is untrue. The _.each() function returns the original list, but _.map() returns a new list. You're directly modifying the original objects as you go, so you end up with references to the same objects in each list, but with _.map() you end up with two separate array instances.