change values in array when doing foreach

example:

var arr = ["one","two","three"];

arr.forEach(function(part){
  part = "four";
  return "four";
})

alert(arr);

The array is still with it's original values, is there any way to have writing access to array's elements from iterating function ?


Solution 1:

The callback is passed the element, the index, and the array itself.

arr.forEach(function(part, index, theArray) {
  theArray[index] = "hello world";
});

edit — as noted in a comment, the .forEach() function can take a second argument, which will be used as the value of this in each call to the callback:

arr.forEach(function(part, index) {
  this[index] = "hello world";
}, arr); // use arr as this

That second example shows arr itself being set up as this in the callback.One might think that the array involved in the .forEach() call might be the default value of this, but for whatever reason it's not; this will be undefined if that second argument is not provided.

(Note: the above stuff about this does not apply if the callback is a => function, because this is never bound to anything when such functions are invoked.)

Also it's important to remember that there is a whole family of similar utilities provided on the Array prototype, and many questions pop up on Stackoverflow about one function or another such that the best solution is to simply pick a different tool. You've got:

  • forEach for doing a thing with or to every entry in an array;
  • filter for producing a new array containing only qualifying entries;
  • map for making a one-to-one new array by transforming an existing array;
  • some to check whether at least one element in an array fits some description;
  • every to check whether all entries in an array match a description;
  • find to look for a value in an array

and so on. MDN link

Solution 2:

Let's try to keep it simple and discuss how it is actually working. It has to do with variable types and function parameters.

Here is your code we are talking about:

var arr = ["one","two","three"];

arr.forEach(function(part) {
  part = "four";
  return "four";
})

alert(arr);

First off, here is where you should be reading about Array.prototype.forEach():

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

Second, let's talk briefly about value types in JavaScript.

Primitives (undefined, null, String, Boolean, Number) store an actual value.

ex: var x = 5;

Reference Types (custom objects) store the memory location of the object.

ex: var xObj = { x : 5 };

And third, how function parameters work.

In functions, parameters are always passed by value.

Because arr is an array of Strings, it's an array of primitive objects, which means they are stored by value.

So for your code above, this means that each time the forEach() iterates, part is equal to the same value as arr[index], but not the same object.

part = "four"; will change the part variable, but will leave arr alone.

The following code will change the values you desire:

var arr = ["one","two","three"];

arr.forEach(function(part, index) {
  arr[index] = "four";
});

alert(arr);

Now if array arr was an array of reference types, the following code will work because reference types store a memory location of an object instead of the actual object.

var arr = [{ num : "one" }, { num : "two"}, { num : "three"}];

arr.forEach(function(part, index) {
  // part and arr[index] point to the same object
  // so changing the object that part points to changes the object that arr[index] points to

  part.num = "four";
});

alert(arr[0].num);
alert(arr[1].num);
alert(arr[2].num);

The following illustrates that you can change part to point to a new object while leaving the objects stored in arr alone:

var arr = [{ num : "one" }, { num : "two"}, { num : "three"}];

arr.forEach(function(part, index) {
  // the following will not change the object that arr[index] points to because part now points at a new object
  part = 5;
});

alert(arr[0].num);
alert(arr[1].num);
alert(arr[2].num);