364

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 ?

Roko C. Buljan
  • 164,703
  • 32
  • 260
  • 278
rsk82
  • 24,158
  • 43
  • 131
  • 223
  • 1
    Related: http://stackoverflow.com/q/6081868/632951 – Pacerier Jan 18 '16 at 01:23
  • 1
    Try map(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map): `x=[2,3,4]; x=x.map(n=>n*2); // [4,6,8]` – Justin May 25 '20 at 03:34

10 Answers10

560

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

Pointy
  • 371,531
  • 55
  • 528
  • 584
  • 51
    Thanks! ES6: arr.forEach((o, i, a) => a[i] = myNewVal) – DiegoRBaquero Jul 12 '17 at 19:40
  • why `part` (or even `o` in es6) is undefined ? how to get iterated value? – Daniil Mashkin Aug 04 '18 at 07:26
  • @DaniilMashkin `part` would be `undefined` if an element of the array has been assigned `undefined` explicitly. "Empty" slots of an array (an array entry that has never been assigned any value) are skipped by `.forEach()` and most other array iteration methods. – Pointy Aug 04 '18 at 12:39
  • 4
    For completeness, `.forEach()` also takes a second argument `thisArg`, which you can use as `this` inside the callback. NOTE: this an argument of `.forEach` NOT an argument of the callback. – Moha the almighty camel Nov 13 '18 at 07:57
  • 4
    To use the `this` passed as second argument to `.forEach()`, you need to pass the callback function using the syntax `function()`, since using ES6's arrow function `() => {}` does not bind context. – jpenna Jan 30 '19 at 03:30
  • `this[index] = "hello world";` doesn't work. `this` refers to something else. – Azevedo Dec 06 '19 at 18:06
  • Seems like `map` is what one might want often in this situation. `x=[2,3,4]; x=x.map( n=>2*n) // [4, 6, 8]`. Simple. – Justin May 25 '20 at 03:32
141

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);
Dave
  • 1,886
  • 1
  • 12
  • 17
  • 2
    indeed, great explanation! It would have been even better to extend the explanation at the other iteration methods, for...of, map, ... The new for...of works in a very similar way than forEach for such cases, except that the "index" is missing for altering eventually the original array (like in forEach). Also ES5 map seems similar to forEach, maybe just that the possibility to return values from a map makes things clearer from a syntax perspective than using the index. – Christophe Vidal Nov 15 '15 at 11:56
  • 1
    Great explanation. Thank you. After all I can't get this statement: ```In functions, parameters are always passed by value.``` What about the second example? – Alex May 20 '16 at 15:47
  • @7sides, That statement is describing that function parameters will always be passed by value. So for primitives, it will be the value that the primitive points to. For objects, it will be the location that the object is pointing to. [this w3schools page](http://www.w3schools.com/js/js_function_parameters.asp) has a good explanation. See sections **Arguments are Passed by Value** and **Objects are Passed by Reference**. – Dave May 20 '16 at 19:21
  • *In functions, parameters are always passed by value.* BAM. thx. +1 – radarbob Nov 24 '17 at 04:22
114

Array: [1, 2, 3, 4]
Result: ["foo1", "foo2", "foo3", "foo4"]

Array.prototype.map() Keep original array

const originalArr = ["Iron", "Super", "Ant", "Aqua"];
const modifiedArr = originalArr.map(name => `${name}man`);

console.log( "Original: %s", originalArr );
console.log( "Modified: %s", modifiedArr );

Array.prototype.forEach() Override original array

const originalArr = ["Iron", "Super", "Ant", "Aqua"];
originalArr.forEach((name, index) => originalArr[index] = `${name}man`);

console.log( "Overridden: %s", originalArr );
Community
  • 1
  • 1
Roko C. Buljan
  • 164,703
  • 32
  • 260
  • 278
16

Javascript is pass by value, and which essentially means part is a copy of the value in the array.

To change the value, access the array itself in your loop.

arr[index] = 'new value';

hvgotcodes
  • 109,621
  • 25
  • 195
  • 231
  • 1
    It depends on the type of `part` whether it is copied - although you are right that the variable is not a pointer, but a value – Bergi Sep 18 '12 at 18:42
  • 8
    Saying "JavaScript is pass by value" is a gross generalization. There are reference types in JavaScript. Value types are passed by value. – Alex Turpin Sep 18 '12 at 18:43
  • 9
    Not a gross generalization. A completely correct and absolute statement. Javascript passes references by value. Javascript is pass by value, always. – hvgotcodes Sep 18 '12 at 18:48
  • @hvgotcodes I was not aware of that. Would you have any links that explained it please? – Alex Turpin Sep 18 '12 at 20:00
  • 1
    @Bergi: No, it does not matter the type. All values are copied on assignment -- the only values in JavaScript are primitives and references. Both primitives and references are copied on assignment. – newacct Sep 19 '12 at 03:08
  • @newacct: OK, but I guess not everyone would consider references to be values :-) – Bergi Sep 19 '12 at 14:49
  • 6
    @Bergi just because a language has a thing called a reference, doesn't mean it uses pass by reference. References can be (and are) passed by value, i.e. the value of the reference is copied and that copy is used as the argument. – hvgotcodes Sep 19 '12 at 15:06
7

Here's a similar answer using using a => style function:

var data = [1,2,3,4];
data.forEach( (item, i, self) => self[i] = item + 10 );

gives the result:

[11,12,13,14]

The self parameter isn't strictly necessary with the arrow style function, so

data.forEach( (item,i) => data[i] = item + 10);

also works.

J. Peterson
  • 1,854
  • 1
  • 24
  • 19
3

replace it with index of the array.

array[index] = new_value;
Asif Alamgir
  • 1,420
  • 1
  • 15
  • 36
3

The .forEach function can have a callback function(eachelement, elementIndex) So basically what you need to do is :

arr.forEach(function(element,index){
    arr[index] = "four";   //set the value  
});
console.log(arr); //the array has been overwritten.

Or if you want to keep the original array, you can make a copy of it before doing the above process. To make a copy, you can use:

var copy = arr.slice();
zhujy_8833
  • 531
  • 4
  • 6
  • 3
    If you want to make a copy of the array, use `map()` instead of `forEach()`. `map()` iterates over the source array and returns a new array containing the [modified] copy of the original: the source array is left unchanged. – Nicholas Carey Dec 02 '14 at 23:55
3

To add or delete elements entirely which would alter the index, by way of extension of zhujy_8833 suggestion of slice() to iterate over a copy, simply count the number of elements you have already deleted or added and alter the index accordingly. For example, to delete elements:

let values = ["A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"];
let count = 0;
values.slice().forEach((value, index) => {
    if (value === "A2" || value === "A5") {
        values.splice(index - count++, 1);
    };
});
console.log(values);

// Expected: [ 'A0', 'A1', 'A3', 'A4', 'A6', 'A7', 'A8' ]

To insert elements before:

if (value === "A0" || value === "A6" || value === "A8") {
    values.splice(index - count--, 0, 'newVal');
};

// Expected: ['newVal', A0, 'A1', 'A2', 'A3', 'A4', 'A5', 'newVal', 'A6', 'A7', 'newVal', 'A8' ]

To insert elements after:

if (value === "A0" || value === "A6" || value === "A8") {
    values.splice(index - --count, 0, 'newVal');
};

// Expected: ['A0', 'newVal', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'newVal', 'A7', 'A8', 'newVal']

To replace an element:

if (value === "A3" || value === "A4" || value === "A7") {
    values.splice(index, 1, 'newVal');
};

// Expected: [ 'A0', 'A1', 'A2', 'newVal', 'newVal', 'A5', 'A6', 'newVal', 'A8' ]

Note: if implementing both 'before' and 'after' inserts, code should handle 'before' inserts first, other way around would not be as expected

Leigh Mathieson
  • 793
  • 1
  • 7
  • 15
2

With the Array object methods you can modify the Array content yet compared to the basic for loops, these methods lack one important functionality. You can not modify the index on the run.

For example if you will remove the current element and place it to another index position within the same array you can easily do this. If you move the current element to a previous position there is no problem in the next iteration you will get the same next item as if you hadn't done anything.

Consider this code where we move the item at index position 5 to index position 2 once the index counts up to 5.

var ar = [0,1,2,3,4,5,6,7,8,9];
ar.forEach((e,i,a) => {
i == 5 && a.splice(2,0,a.splice(i,1)[0])
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 5 5 - 6 6 - 7 7 - 8 8 - 9 9

However if we move the current element to somewhere beyond the current index position things get a little messy. Then the very next item will shift into the moved items position and in the next iteration we will not be able to see or evaluate it.

Consider this code where we move the item at index position 5 to index position 7 once the index counts up to 5.

var a = [0,1,2,3,4,5,6,7,8,9];
a.forEach((e,i,a) => {
i == 5 && a.splice(7,0,a.splice(i,1)[0])
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 5 5 - 6 7 - 7 5 - 8 8 - 9 9

So we have never met 6 in the loop. Normally in a for loop you are expected decrement the index value when you move the array item forward so that your index stays at the same position in the next run and you can still evaluate the item shifted into the removed item's place. This is not possible with array methods. You can not alter the index. Check the following code

var a = [0,1,2,3,4,5,6,7,8,9];
a.forEach((e,i,a) => {
i == 5 && (a.splice(7,0,a.splice(i,1)[0]), i--);
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 4 5 - 6 7 - 7 5 - 8 8 - 9 9

As you see when we decrement i it will not continue from 5 but 6, from where it was left.

So keep this in mind.

Redu
  • 19,106
  • 4
  • 44
  • 59
0

You can try this if you want to override

var newArray= [444,555,666];
var oldArray =[11,22,33];
oldArray.forEach((name, index) => oldArray [index] = newArray[index]);
console.log(newArray);
Shuhad zaman
  • 2,298
  • 22
  • 25