change values in array when doing foreach

JavascriptArraysForeachPass by-Reference

Javascript Problem Overview


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 ?

Javascript Solutions


Solution 1 - Javascript

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 - Javascript

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);

Solution 3 - Javascript

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 );

Solution 4 - Javascript

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';

Solution 5 - Javascript

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.

Solution 6 - Javascript

replace it with index of the array.

array[index] = new_value;

Solution 7 - Javascript

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();

Solution 8 - Javascript

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

Solution 9 - Javascript

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.

Solution 10 - Javascript

If you want to remove items

If the change you need to make is to completely remove one or more items from the list you're safer using a for loop and going backwards through the loop.

    for (let i = myArray.length - 1; i >= 0; i--)
    {
        const item = myArray[i];

        if (...) 
        {
           // remove item
           // https://stackoverflow.com/questions/5767325/how-can-i-remove-a-specific-item-from-an-array?rq=1
        }
    };

Going backwards means that the array of each item never changes. If you are going forwards through a loop and delete item[3] then item[4] is now the new item[3], which doesn't make anything easier. You won't have that issue going backwards.

This is of course a solution not using foreach, but it's important to remember that 'old school' ways can often be best. And if for whatever reason you need to break; from the loop (and there are more than a handful of items) a for loop is more efficient since you can't break out of a for loop.

Solution 11 - Javascript

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);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionrsk82View Question on Stackoverflow
Solution 1 - JavascriptPointyView Answer on Stackoverflow
Solution 2 - JavascriptDaveView Answer on Stackoverflow
Solution 3 - JavascriptRoko C. BuljanView Answer on Stackoverflow
Solution 4 - JavascripthvgotcodesView Answer on Stackoverflow
Solution 5 - JavascriptJ. PetersonView Answer on Stackoverflow
Solution 6 - JavascriptAsif AlamgirView Answer on Stackoverflow
Solution 7 - Javascriptzhujy_8833View Answer on Stackoverflow
Solution 8 - JavascriptLeigh MathiesonView Answer on Stackoverflow
Solution 9 - JavascriptReduView Answer on Stackoverflow
Solution 10 - JavascriptSimon_WeaverView Answer on Stackoverflow
Solution 11 - JavascriptShuhad zamanView Answer on Stackoverflow