95

The only difference I see in map and foreach is that map is returning an array and forEach is not. However, I don't even understand the last line of the forEach method "func.call(scope, this[i], i, this);". For example, isn't "this" and "scope" referring to same object and isn't this[i] and i referring to the current value in the loop?

I noticed on another post someone said "Use forEach when you want to do something on the basis of each element of the list. You might be adding things to the page, for example. Essentially, it's great for when you want "side effects". I don't know what is meant by side effects.

Array.prototype.map = function(fnc) {
    var a = new Array(this.length);
    for (var i = 0; i < this.length; i++) {
        a[i] = fnc(this[i]);
    }
    return a;
}

Array.prototype.forEach = function(func, scope) {
    scope = scope || this;
    for (var i = 0, l = this.length; i < l; i++) {
        func.call(scope, this[i], i, this);
    }
}

Finally, are there any real uses for these methods in JavaScript (since we aren't updating a database) other than to manipulate numbers like the following?

alert([1,2,3,4].map(function(x){ return x + 1})); // This is the only example I ever see of map in JavaScript.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
JohnMerlino
  • 3,900
  • 4
  • 57
  • 89
  • How can one find a function's definition of native JavaScript, like you did with `map` and `forEach`? All that I get from Google is usage specifications and tutorials. – WoodrowShigeru Oct 10 '17 at 10:47
  • See also the language-agnostic [Is there a difference between foreach and map?](https://stackoverflow.com/q/354909/1048572) – Bergi Aug 19 '20 at 20:51

8 Answers8

102

The essential difference between map and forEach in your example is that forEach operates on the original array elements, whereas map explicitly returns a new array as a result.

With forEach you are taking some action with -- and optionally changing -- each element in the original array. The forEach method runs the function you provide for each element, but returns nothing (undefined). On the other hand, map walks through the array, applies a function to each element, and emits the result as a new array.

The "side effect" with forEach is that the original array is being changed. "No side effect" with map means that, in idiomatic usage, the original array elements are not changed; the new array is a one-to-one mapping of each element in the original array -- the mapping transform being your provided function.

The fact that there's no database involved does not mean that you won't have to operate on data structures, which, after all, is one of the essences of programming in any language. As for your last question, your array can contain not only numbers, but objects, strings, functions, etc.

Ken Redler
  • 23,863
  • 8
  • 57
  • 69
  • 6
    note from the future: this answer is actually nonsense; `.map` can do everything `.forEach` can do (including modifying elements), it just returns a new list constructed from the iterator function. – Travis Webb May 12 '15 at 05:06
  • 7
    Responding from the past: I respectfully disagree. `.map()` creates a new array, and does not alter the original. You could indeed modify the array that is itself being mapped, but that would be at the very least non-idiomatic, if not nonsensical. `.forEach()`, while similar, applies its function to each element, but always returns `undefined`. Notwithstanding all the above, the OP is also asking about his own specific functions added to the array prototype; there was no ES5 back here in the past. – Ken Redler May 13 '15 at 15:45
  • 5
    There’s still nothing that `forEach` does that you can’t do with `map`. You could simply not care about the return value from `map` and you’d have a `forEach`. The difference is that it’s super ineffective to use `map` for tasks where you don’t want to create a new array based on results. So for those, you use `forEach`. – poke Dec 23 '15 at 00:07
  • 3
    @poke: ..and likewise, you could do whatever you need with a plain old `for` loop. I don't disagree that there's some difference in "effectiveness", but also I don't think anyone's arguing that one has some magic the other can't somehow achieve. Although there is, in fact, one thing that `map` cannot do: not return an array. There's value in having JS live up to expectations from other languages as to the behavior of functional favorites like `map`, `reduce`, `filter`, etc. E.g., you could implement `reduceRight` using similar building blocks, but why not just use `reduceRight`? – Ken Redler Dec 23 '15 at 16:47
  • I think this is misleading because unless your map closure returns an element, you can easily modify the original array if you're not careful. – maschwenk Apr 06 '16 at 06:01
  • @maschwenk, how do you mean? If the function passed to `map` returns nothing (i.e. `undefined`), you'd end up emitting a new array of `undefined` elements. The original array would remain unchanged unless you really went out of your way to change it. – Ken Redler Apr 06 '16 at 20:52
  • look at the first example in @nrabinowitz answer – maschwenk Apr 06 '16 at 21:58
  • @mashwenk, I think this is just a matter of your viewing the modification of original array elements as being easy to do if you're not careful, and my viewing it as departing from the idiom enough to be something you'd have to go out of your way to do. Likewise, @ nrabinowitz just considers it "ugly". We're all in agreement it's possible; "easy" vs "awkward" vs "ugly" are matters of opinion and style. As a side note, it's amusing how much voting activity, commentary, and even occasional vitriol (I don't mean your comment) this question generates. – Ken Redler Apr 07 '16 at 18:00
  • The output of `a=[{val:1},{val:2},{val:3},{val:4}]; a.map((b)=>{b.val=b.val**2}); JSON.stringify(a);` is `[{"val":1},{"val":4},{"val":9},{"val":16}]`. I'm not sure if you're wrong or merely being unclear, but `.map()` does operate on the original array elements, just as `.forEach()` does, so I'm not sure why you specifically make that point with `.forEach()`. – Chinoto Vokro Aug 26 '16 at 05:38
  • 1
    @ChinotoVokro, your example is *explicitly mutating* the original array by assigning `b.val = b.val**2` *inside the returned object*. That's the precise thing we're saying is indeed possible but confusing and non-idiomatic. Normally you simply return the new value, not also assign it to the original array: `a=[{val:1},{val:2},{val:3},{val:4}]; a.map((b)=>{b.val**2}); JSON.stringify(a);` – Ken Redler Oct 19 '17 at 17:39
  • @KenRedler I was confused by my own comment until I thought to look at your edit history. When I originally commented on this, there was no mention of "idiomatic usage", just that `.forEach()` operates on the original array elements, which in my mind implied that `.map()` somehow doesn't, so I was showing that it can. You are certainly correct that, idiomatically, map shouldn't change the original array. – Chinoto Vokro Jun 30 '18 at 14:37
  • 1
    I think the take-home is that you can't *return a new array* from a call to `forEach` and chain operations [dare he say "*[LINQ](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/getting-started-with-linq)-style*"?], which means **`map`'s behavior lends itself to functional logic in a way `forEach` simply can't.** If you *want* to be anti-pattern with `map`, you can, of course, but... why? Why not use the right tool for your job? That's precisely why we have `forEach` & `for`. Self-comment your code, please. Someday, someone else might have to maintain it. ;^) – ruffin Jul 27 '18 at 19:23
  • 1
    The discussion on this thread is gold (and I'm not even being sarcastic) lol – AndrewL64 Jan 04 '19 at 19:01
  • @AndrewL64 I stopped answering questions on SO ages ago; this is the only one that continually attracts new comments. There have even been a bunch of comments that have had to be removed because they violated community rules for polite discourse. People literally insulting each other. It's quite enjoyable. – Ken Redler Jan 24 '19 at 22:32
  • @ruffin While clarity may be a legitimate concern, i question your phrase "use the right tool for your job". Aside from the fact that one returns and array and the other doesn't, what you're talking about isn't about "right too", it's about "right name", ie clarity. In terms of effectiveness at the job, if you're not trying to return an array, they are both equally effective tools for the job. – johny why Nov 18 '22 at 08:23
  • Aside from map and forEach, there are myriad functions one can create, some which return something, some which don't, some which have side-effects, some which don't, with *no clues* given to someone reading the code (like "oh, they're using map, so this is supposed to return an array"). In myriad cases, there are no available clues as to whether or not a function returns something other than *reading the code and comments*. So it seems a bit absurd to be so strict about this one limited case (map vs forEach) when so much other code doesn't offer such signposts. – johny why Nov 18 '22 at 08:45
  • @johnywhy That's a self-defeating take: _there are no available clues as to whether or not a function returns something other than reading the code and comments._ Exactly. Let someone read [`map` (or `filter` or `find` or...)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#array_methods_and_empty_slots) when you mean to map (or filter or find or...) an array & `foreach` when you want to act w/out effects covered by the language specs. Words have meaning. But [enjoy your broken windows](https://blog.codinghorror.com/the-broken-window-theory/). – ruffin Nov 18 '22 at 17:59
  • @ruffin The reason, as a coder, using map for everything appeals to me is because that would make my programming tasks simpler. map does everything forEach can do, as performant or better. I think your broken window article doesn't mean what you think it means -- it's about bugs, not programming philosophy. No one here says using map in place of forEach is a bug, they just feel it's less *self-documenting*. But i already use meaningful function names, meaningful variable names, and rich commenting. In some languages, there's only ONE iteration statement -- people manage. – johny why Nov 18 '22 at 18:34
75

The main difference between the two methods is conceptual and stylistic: You use forEach when you want to do something to or with each element of an array (doing "with" is what the post you cite meant by "side-effects", I think), whereas you use map when you want to copy and transform each element of an array (without changing the original).

Because both map and forEach call a function on each item in an array, and that function is user-defined, there is almost nothing you can do with one and not with the other. It's possible, though ugly, to use map to modify an array in-place and/or do something with array elements:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.map(function(el) {
    el.val++; // modify element in-place
    alert(el.val); // do something with each element
});
// a now contains [{ val: 2 }, { val: 3 }, { val: 4 }]

but much cleaner and more obvious as to your intent to use forEach:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.forEach(function(el) {
    el.val++;
    alert(el.val);
});

Especially if, as is usually the case in the real world, el is a usefully human-readable variable:

cats.forEach(function(cat) {
    cat.meow(); // nicer than cats[x].meow()
});

In the same way, you can easily use forEach to make a new array:

var a = [1,2,3],
    b = [];
a.forEach(function(el) {
    b.push(el+1);
});
// b is now [2,3,4], a is unchanged

but it's cleaner to use map:

var a = [1,2,3],
    b = a.map(function(el) {
        return el+1;
    });

Note as well that, because map makes a new array, it likely incurs at least some performance/memory hit when all you need is iteration, particularly for large arrays - see http://jsperf.com/map-foreach

As for why you'd want to use these functions, they're helpful any time you need to do array manipulation in JavaScript, which (even if we're just talking about JavaScript in a browser environment) is pretty often, almost any time you're accessing an array that you're not writing down by hand in your code. You might be dealing with an array of DOM elements on the page, or data pulled from an Ajax request, or data entered in a form by the user. One common example I run into is pulling data from an external API, where you might want to use map to transform the data into the format you want and then use forEach to iterate over your new array in order to display it to your user.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
nrabinowitz
  • 55,314
  • 10
  • 149
  • 165
  • i see people using `.map()` all the time to modify inline elements. I was under the impression that was the main benefit of using `.map` over `.forEach` – chovy Oct 07 '16 at 06:51
  • 3
    @chovy I believe that you should choose based on whether you want to make an array of results. `.map` can modify elements, yes, but it would create an array you don't need. You should also consider how choosing one or the other lets the reader guess whether or not you have side-effects – leewz Oct 26 '16 at 03:09
  • "use forEach when you want to do something to or with each element of an array" -- Since clarity is important to you, i suggest it may create confusion to say that you're doing something "to" array. Doing anything TO the elements of the original array that you're looping over seems very ill-advised. – johny why Nov 18 '22 at 08:27
  • 1
    @johnywhy While it may not be a good idea to mutate elements of the input array, it's not categorically wrong, and there are many cases where it may make sense. E.g. `caches.forEach(cache => cache.invalidate())` – nrabinowitz Dec 01 '22 at 18:42
17

The voted answer (from Ken Redler) is misleading.

A side effect in computer science means that a property of a function/method alters a global state [Wikipedia]. In some narrow sense, this may also include reading from a global state, rather than from arguments. In imperative or OO programming, side effects appear most of the time. And you are probably making use of it without realizing.

The significant difference between forEach and map is that map allocates memory and stores the returning value, while forEach throws it away. See the ECMA specification for more information.

As for the reason why people say forEach is used when you want a side effect is that the return value of forEach is always undefined. If it has no side effect (does not change global state), then the function is just wasting CPU time. An optimizing compiler will eliminate this code block and replace the it with the final value (undefined).

By the way, it should be noted that JavaScript has no restriction on side effects. You can still modify the original array inside map.

var a = [1,2,3]; //original
var b = a.map( function(x,i){a[i] = 2*x; return x+1} );
console.log("modified=%j\nnew array=%j",a,b);
// output:
// modified=[2,4,6]
// new array=[2,3,4]
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jack Tang
  • 486
  • 6
  • 7
  • 2
    This question and its answers and comments are always fun to circle back on every couple of years. The way map is implemented in JS, in terms of memory allocation, is another nice angle. – Ken Redler Oct 26 '19 at 23:46
7

This is a beautiful question with an unexpected answer.

The following is based on the official description of Array.prototype.map().

There is nothing that forEach() can do that map() cannot. That is, map() is a strict super-set of forEach().

Although map() is usually used to create a new array, it may also be used to change the current array. The following example illustrates this:

var a = [0, 1, 2, 3, 4], mapped = null;
mapped = a.map(function (x) { a[x] = x*x*x; return x*x; });
console.log(mapped); // logs [0, 1, 4, 9, 16]  As expected, these are squares.
console.log(a); // logs [0, 1, 8, 27, 64] These are cubes of the original array!!

In the above example, a was conveniently set such that a[i] === i for i < a.length. Even so, it demonstrates the power of map(), and in particular its ability to change the array on which it is called.

Note1:
The official description implies that map() may even change length the array on which it is called! However, I cannot see (a good) reason to do this.

Note 2:
While map() map is a super-set of forEach(), forEach() should still be used where one desires the change a given array. This makes your intentions clear.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sumukh Barve
  • 1,414
  • 15
  • 12
  • 8
    Actually there is 1 thing that forEach can do that map can't do - not return an array. – Antti Haapala -- Слава Україні Jul 29 '14 at 11:10
  • 1
    You can also use the third argument to the mapping function to mutate the target array, instead of the scoped variable: `mapped = a.map(function (x, i, arr) { arr[i] = x * x * x; return x * x; });.` – pdoherty926 Sep 06 '14 at 00:40
  • @pdoherty926 true, but so can `a.forEach(function(x, i, arr) { ...`, which, again, is a conceptually more correct way when you there is an intention to _mutate_ each original item as opposed to _map_ values from one array to another – conny Feb 25 '16 at 05:16
  • @conny: Agreed. Also, please see [this answer](http://stackoverflow.com/questions/354909/is-there-a-difference-between-foreach-and-map/25010698#25010698), to a similar question. – Sumukh Barve Feb 25 '16 at 19:20
3

You can use map as though it were forEach.

It will do more than it has to, however.

scope can be an arbitrary object; it's by no means necessarily this.

As for whether there are real uses for map and forEach, as well to ask if there are real uses for for or while loops.

wombleton
  • 8,336
  • 1
  • 28
  • 30
  • But why is both "scope" and "this" being called here: func.call(scope, this[i], i, this); Isn't scope a parameter that is equal to the current object, which is "this"? – JohnMerlino Jun 14 '10 at 17:07
  • No, it *can* be equal to the current object. The object itself is passed as the third parameter to the array. `scope = scope || this` means "if `scope` is falsy (undefined, null, false, etc) set scope to `this` instead and carry on". – wombleton Jun 15 '10 at 01:16
  • Can you link me to an example when it's not equal to this? – JohnMerlino Jun 15 '10 at 12:41
  • https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/forEach has one under "Printing the contents of an array with an object method" – wombleton Jun 16 '10 at 02:49
  • map will do more than it has to, and yet various people on SO reported superior performance over forEach. Pretty good deal for the money. – johny why Nov 18 '22 at 08:25
2

While all the previous questions are correct, I would definitely make a different distinction. The use of map and forEach can imply intent.

I like to use map when I am simply transforming the existing data in some way (but want to make sure the original data is unchanged).

I like to use forEach when I am modifying the collection in place.

For instance,

var b = [{ val: 1 }, { val: 2 }, { val: 3 }];
var c = b.map(function(el) {
    return { val: el.val + 1 }; // modify element in-place
});
console.log(b);
//  [{ val: 1 }, { val: 2 }, { val: 3 }]
console.log(c);
//  [{ val: 3 }, { val: 4 }, { val: 5 }]

My rule of thumb being making sure when you map you are always creating some new object/value to return for each element of the source list and returning it rather than just performing some operation on each element.

Unless you have any real need to modify the existing list, it doesn't really make sense to modify it in place and fits better into functional/immutable programming styles.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
maschwenk
  • 569
  • 1
  • 9
  • 20
1

TL;DR answer --

map always returns another array.

forEach does not. It is up to you to decide what it does. Return an array if you want or do something else if you don't.

Flexibility is desirable is certain situations. If it isn't for what you are dealing with then use map.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
charsi
  • 2,917
  • 22
  • 40
0

Others have already posted about your main question regarding the difference between the functions. But for...

are there any real uses for these methods in JavaScript (since we aren't updating a database) other than to manipulate numbers like this:

...it's funny you should ask. Just today I wrote a piece of code that assigns a number of values from a regular expression to multiple variables using map for transformation.

It was used to convert a very complicated text-based structure into visualizable data ... but for simplicity's sake, I shall offer an example using date strings, because those are probably more familiar for everyone (though, if my problem had actually been with dates, instead of map I would've used Date-object, which would've done the job splendidly on its own).

const DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})Z$/;
const TEST_STRING = '2016-01-04T03:20:00.000Z';

var [
    iYear,
    iMonth,
    iDay,
    iHour,
    iMinute,
    iSecond,
    iMillisecond
    ] = DATE_REGEXP
        // We take our regular expression and...
        .exec(TEST_STRING)
        // ...execute it against our string (resulting in an array of matches)...
        .slice(1)
        // ...drop the 0th element from those (which is the "full string match")...
        .map(value => parseInt(value, 10));
        // ...and map the rest of the values to integers...

// ...which we now have as individual variables at our perusal
console.debug('RESULT =>', iYear, iMonth, iDay, iHour, iMinute, iSecond, iMillisecond);

So ... while this was just an example - and only did a very basic transformation for the data (just for sake of example) ... having done this without map would've been a much more tedious task.

Granted, it is written in a version of JavaScript that I don't think too many browsers support yet (at least fully), but - we're getting there. If I needed to run it in browser, I believe it would transpile nicely.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Markku Uttula
  • 126
  • 1
  • 3