Splicing a Javascript array from within the callback passed to forEach
Lets see why JavaScript behaves like this. According to the ECMAScript standard specification for Array.prototype.forEach
,
when you delete an element at index 1, the element at index 2 becomes the element at index 1 and index 2 doesn't exist for that object.
Now, JavaScript looks for element 2 in the object, which is not found, so it skips the function call.
That is why you see only a
and b
.
The actual way to do this, is to use
Array.prototype.filter
var array = ["a", "b", "c"];
array = array.filter(function(currentChar) {
console.log(currentChar); // a, b, c on separate lines
return currentChar !== "b";
});
console.log(array); // [ 'a', 'c' ]
Splice on forEach loop not working properly
Your code is splicing while looping. Spliced elements are accessed even they are no more existing. That leads to undefined elements.
You may consider Array#filter
var obj = [{ "index": 0, "name": "Odonnell Noble", "gender": "male", "company": "DIGIQUE", "eail": "odonnellnoble@digique.com" }, { "index": 1, "name": "Marie Oneal", "gender": "female", "company": "CANOPOLY", "email": "marieoneal@canopoly.com" }, { "index": 2, "name": "Adrienne Marsh", "gender": "female", "company": "XOGGLE", "email": "adriennemarsh@xoggle.com" }, { "index": 3, "name": "Goff Mullins", "gender": "male", "company": "ENDIPIN", "email": "goffmullins@endipin.com" }, { "index": 4, "name": "Lucile Finley", "gender": "female", "company": "AQUASSEUR", "email": "lucilefinley@aquasseur.com" }, { "index": 5, "name": "Pitts Mcpherson", "gender": "male", "company": "QUARX", "email": "pittsmcpherson@quarx.com" }];
obj = obj.filter(function (user, index) { return (user.index !== 5 && user.index !== 2);});
document.write('<pre>' + JSON.stringify(obj, 0, 4) + '</pre>');
JavaScript splice function inside foreach loop decrements index
You splice elements from the array, which you iterated, therefore indexes in "todos" reduced. Sorry for my bad english.
var notDonedTodos = [];
angular.forEach($scope.todos, function(todo, i)
{
if(!todo.done)
{
notDonedTodos.push(todo);
}
});
$scope.todos = notDonedTodos;
JS - filter v.s. splice in forEach loop
That is simply how Array.prototype.splice, Array.prototype.filter and Array.prototype.forEach are defined. splice
is meant to modify the array (and return removed values) while filter
is meant to return a copy of the array with only the matching values
Array.prototype.forEach iterates over the array in ascending index order. Even though you modify the array during e.g. index 3, next iteration it'll just go to the next index, e.g. 4, unless the end of the array is already reached. Mind that once .forEach
is called on an array, it'll keep working on that array. You setting arr
to another value doesn't affect the array nor the state of the .forEach
call. Similar to a regular function call:
let someVariable = 5;
function test(param) {
someVariable = 6;
console.log(param); // still prints 5
}
test(someVariable);
After all, JavaScript works by reference (or primitive values), you're not passing a pointer to the someVariable
variable like you can do in some other languages. Is an array.forEach with a splice the best way to remove an entry from an array of unique Ids?
var id = 99;
self.tests.some(function (elem, index) {
if (elem['userTestId'] === id)
self.tests.splice(index, 1);
return true;
});
return false;
}
Could utilise Array.some
? Stops looping once you return TRUE from a callback. Does the Array.prototype.forEach() method allow the skipping of iterations by modifying the index parameter?
The index
is limited to the scope of the multiplyFives()
function and won't modify the index of the array.forEach()
loop.
With your code, when a new element is spliced onto the array at the next index, the array.forEach()
loop will see that element in the next iteration and continue splicing the same element for the remaining length of the array.
A boolean duplicate
flag is required to allow duplicating adjacent 5 values and to ignore values that were already duplicated.
Here's the correct JavaScript code with comments explaining each modified line.
const array = [1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10]
console.log('Array without duplicated 5 values')
console.log(array)
function duplicateFives(array) {
// Set a boolean flag to determine if a duplicate 5 should be made
let duplicate = true
function multiplyFives (element, index) {
if (element === 5) {
if (duplicate === true) {
// Add a duplicate 5 to the array if the current element isn't from a previous duplication
array.splice(index, 0, 5)
// Prevent the duplicated 5 from duplicating itself in the next iteration
duplicate = false
} else {
// Allow duplication for the next element with a value of 5 in the array
duplicate = true
}
}
}
array.forEach(multiplyFives)
return array
}
duplicateFives(array)
console.log('Array with duplicated 5 values')
console.log(array)
change values in array when doing foreach
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
How can i splice current index in a foreach?
.splice()
accepts a start index and an amount of items to remove, not the object you want to remove from the array.
You want to do this:Parameters
startIndex:int
— An integer that specifies the index of the element in the array where the insertion or deletion begins. You can use a negative integer to specify a position relative to the end of the array (for example, -1 is the last element of the array).
deleteCount:uint
— An integer that specifies the number of elements to be deleted. This number includes the element specified in the startIndex parameter. If you do not specify a value for the deleteCount parameter, the method deletes all of the values from the startIndex element to the last element in the array. If the value is 0, no elements are deleted.
var index:int = platformsCloud.indexOf(platformCloud);
platformsCloud.splice(index, 1);
Loop (for each) over an array in JavaScript
TL;DRYour best bets are usually
- a
for-of
loop (ES2015+ only; spec | MDN) - simple andasync
-friendlyfor (const element of theArray) {
// ...use `element`...
} forEach
(ES5+ only; spec | MDN) (or its relativessome
and such) - notasync
-friendly (but see details)theArray.forEach(element => {
// ...use `element`...
});- a simple old-fashioned
for
loop -async
-friendlyfor (let index = 0; index < theArray.length; ++index) {
const element = theArray[index];
// ...use `element`...
} - (rarely)
for-in
with safeguards -async
-friendlyfor (const propertyName in theArray) {
if (/*...is an array element property (see below)...*/) {
const element = theArray[propertyName];
// ...use `element`...
}
}
- a
Some quick "don't"s:
- Don't use
for-in
unless you use it with safeguards or are at least aware of why it might bite you. - Don't use
map
if you're not using its return value.
(There's sadly someone out there teachingmap
[spec / MDN] as though it wereforEach
— but as I write on my blog, that's not what it's for. If you aren't using the array it creates, don't usemap
.) - Don't use
forEach
if the callback does asynchronous work and you want theforEach
to wait until that work is done (because it won't).
- Don't use
JavaScript has powerful semantics for looping through arrays and array-like objects. I've split the answer into two parts: Options for genuine arrays, and options for things that are just array-like, such as the
arguments
object, other iterable objects (ES2015+), DOM collections, and so on.Okay, let's look at our options:
For Actual Arrays
You have five options (two supported basically forever, another added by ECMAScript 5 ["ES5"], and two more added in ECMAScript 2015 ("ES2015", aka "ES6"):
- Use
for-of
(use an iterator implicitly) (ES2015+) - Use
forEach
and related (ES5+) - Use a simple
for
loop - Use
for-in
correctly - Use an iterator explicitly (ES2015+)
Details:
1. Use for-of
(use an iterator implicitly) (ES2015+)
ES2015 added iterators and iterables to JavaScript. Arrays are iterable (so are strings, Map
s, and Set
s, as well as DOM collections and lists, as you'll see later). Iterable objects provide iterators for their values. The new for-of
statement loops through the values returned by an iterator:const a = ["a", "b", "c"];
for (const element of a) { // You can use `let` instead of `const` if you like
console.log(element);
}
// a
// b
// c
Related Topics
Rendering to Js with Jinja Produces Invalid Number Rather Than String
React Routing Works in Local MAChine But Not Heroku
JavaScript Ternary Operator Example with Functions
How to Check If an Embedded Svg Document Is Loaded in an HTML Page
JavaScript Settimeout and Loops
What's "This" in JavaScript Onclick
How to Show a Spinner While Loading an Image via JavaScript
Detect Whether Scroll Event Was Created by User
How to Subscribe to Topics with Web Browser Using Firebase Cloud Messaging
Validating User's Utf-8 Name in JavaScript
Why Is 'This' Undefined Inside Class Method When Using Promises
Using Filesystem in Node.Js with Async/Await
How to Deeply Merge Two Object Values by Keys
Backbone: Why Assign '$('#Footer')' to 'El'
Passing Data to a Jquery UI Dialog
Objects Are Not Valid as a React Child (Found: [Object Promise])