Skip to content

Instantly share code, notes, and snippets.

@drzhbe
Last active April 17, 2023 10:17
Show Gist options
  • Save drzhbe/5554720 to your computer and use it in GitHub Desktop.
Save drzhbe/5554720 to your computer and use it in GitHub Desktop.
Как удалить элемент массива при переборе элементов массива или что бывает, если использовать метод splice внутри цикла? Описание можно почитать в комментарии к гисту.
// Bad method, don't use it.
var a = [0,1,2,3,4]
for (var i = 0; i < a.length; i++) {
console.log(i, a[i]);
if (a[i] === 0) {
console.log(a);
a.splice(i, 1);
console.log(a);
}
}
/*
0 0
[0, 1, 2, 3, 4]
[1, 2, 3, 4] // after splice we are in trap: next iteration i will be 1 and a[1] will be 2, so we missed value 1
1 2
2 3
3 4
*/
// Good method.
var a = [0,1,2,3,4]
for (var i = 0; i < a.length; i++) {
console.log(i, a[i]);
if (a[i] === 0) {
console.log(a);
a.splice(i, 1);
i--;
console.log(a);
}
}
/*
0 0
[0, 1, 2, 3, 4]
[1, 2, 3, 4]
0 1
1 2
2 3
3 4
*/
var a = [0,1,2,3,4]
for (var i = 0; i < a.length; i++) {
console.log(i, a[i]);
if (a[i] === 0) {
console.log(a);
delete a[i];
console.log(a);
}
}
/*
0 0
[0, 1, 2, 3, 4]
[1: 1, 2: 2, 3: 3, 4: 4] // as we see, now we haven't got first element
1 1
2 2
3 3
4 4
*/
/*
But what actualy was happen?
Let's print to console just variable name
-> a
will print
[undefined × 1, 1, 2, 3, 4]
and if we will iterate again it will print our 5 elements including first undefined.
*/
for (var i = 0; i < a.length; i++) {
console.log(i, a[i]);
}
/*
0 undefined
1 1
2 2
3 3
4 4
*/
@drzhbe
Copy link
Author

drzhbe commented May 10, 2013

Как удалить элемент массива при переборе элементов массива или что бывает, если использовать метод splice внутри цикла?

Мы пропустим, как минимум 1 элемент* и перебор получится не полным. Потому что splice удаляет элемент массива и переиндексирует массив. И все элементы, которые стояли после жертвы получают index - 1.

Предположим, у нас есть массив a = [0, 1, 2];
И на первой итерации мы делаем a.splice( i, 1 ), где i = 0.
Теперь наш массив имеет вид [1, 2];
Следующая итерация, где i = 1, и если теперь обратиться к a[ i ], получим значение 2 и это наша последняя итерация**.
Получается мы прошли мимо элемента массива со значением 1. Наверняка это не то, чего мы ждали.

Чтоб не пропустить элементы при обходе массива можно в теле цикла сразу за методом array.splice( i, 1) сделать i--, тогда при следующей итерации i будет равен столько же, сколько и в текущей, но с учетом удаления одного элемента, этот индекс будет указывать на следующий элемент.

* если, конечно, splice не произойдет на последней итерации цикла, тогда мы ничего не потеряем. В то же время мы пропустим больше, чем 1 элемент, если по условиям splice отработает несколько раз.
** но если мы кэшируем длину массива for( var i = 0, len = a.length; i < len; i++ ), то итераций будет столько, сколько элементов в массиве было изначально, но в последней итерации, где i = 2, значение a[ i ] будет undefined

Оператор delete

Если мы попробуем удалить элемент массива при помощи оператора delete, он оставит на места элемента значение undefined, и количество элементов массива не уменьшится. В этом случае обход массива будет правильным, но вместо удаления элемента массива мы, как бы, обнулим значение этого элемента.
Такое ощущение, что оператор delete больше предназначен для удаления свойств объектов. Там он работает как часы.

@fobos
Copy link

fobos commented May 13, 2013

Оператор delete именно и предназначен для удаления свойств объекта

@fobos
Copy link

fobos commented May 13, 2013

Стаст, как вариант можно использовать копию массива, либо несколько переменных-счётчиков :) Всё зависит от конкретной задлачи, которую ты решаешь

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment