Обход массива и удаление элементов без нарушения цикла for
Я столкнулся с проблемой в следующем цикле for
. При использовании splice()
для удаления элемента у меня возникает ошибка, что переменная seconds
становится неопределенной. Я мог бы проверить, не является ли она неопределенной, но мне кажется, что существует более элегантное решение этой проблемы. Я хочу просто удалить элемент и продолжить выполнение цикла.
Вот мой код:
for (i = 0, len = Auction.auctions.length; i < len; i++) {
auction = Auction.auctions[i];
Auction.auctions[i]['seconds'] --;
if (auction.seconds < 0) {
Auction.auctions.splice(i, 1);
}
}
Как можно решить эту проблему?
5 ответ(ов)
Массив переиндексируется, когда вы используете метод .splice()
, что приводит к пропуску индексов при удалении элемента, и ваше кэшированное значение .length
становится устаревшим.
Чтобы исправить это, вам нужно либо уменьшать i
после вызова .splice()
, либо просто итераровать в обратном порядке...
var i = Auction.auctions.length;
while (i--) {
...
if (...) {
Auction.auctions.splice(i, 1);
}
}
Таким образом, переиндексирование не повлияет на следующий элемент в итерации, поскольку изменение индексов затрагивает только элементы от текущей позиции до конца массива, а следующий элемент в итерации будет иметь индекс ниже текущей позиции.
Это довольно распространенная проблема. Решение заключается в том, чтобы перебирать элементы в обратном порядке:
for (var i = Auction.auctions.length - 1; i >= 0; i--) {
Auction.auctions[i].seconds--;
if (Auction.auctions[i].seconds < 0) {
Auction.auctions.splice(i, 1);
}
}
Не имеет значения, если вы удаляете элементы с конца массива, потому что индексы сохранятся при проходе в обратном порядке.
Вместо того чтобы рассчитывать длину массива только в начале цикла, вы можете перерасчитывать ее на каждой итерации. Например:
for (let i = 0; i < Auction.auctions.length; i++) {
let auction = Auction.auctions[i];
auction.seconds--;
if (auction.seconds < 0) {
Auction.auctions.splice(i, 1);
i--; // уменьшаем индекс
}
}
Таким образом, вы не выйдете за пределы массива.
EDIT: В добавление, я добавил уменьшение i
в условие if
.
Данный фрагмент кода используется для фильтрации массива аукционов Auction.auctions
. Он проверяет, осталось ли время (в секундах) для каждого аукциона. Если значение el["seconds"]
, соответствующее каждому аукциону, больше нуля после уменьшения на единицу, то аукцион остается в массиве. В противном случае он удаляется.
Вот как это работает:
- Метод
filter
создает новый массив, включающий только те элементы, для которых переданная функция возвращаетtrue
. - Внутри функции мы уменьшаем значение
seconds
на 1 с помощью postfix операции--
, затем проверяем, больше ли оно нуля. - Если
seconds
больше нуля, значит аукцион все еще активен, и он останется в новом массиве.
Таким образом, код эффективно удаляет все аукционы, у которых время истекло.
Если вы используете ES6 и выше, то почему бы не воспользоваться методом Array.filter
? Этот метод позволяет вам легко фильтровать массивы. В вашем случае можно сделать следующее:
Auction.auctions = Auction.auctions.filter((auction) => {
auction['seconds']--; // Уменьшаем значение секунда
return auction.seconds > 0; // Фильтруем по условию
});
Обратите внимание, что изменение элемента массива во время итерации с помощью filter
будет работать только для объектов, как в вашем примере. Если вы работаете с массивом примитивных значений (например, числами или строками), то это не сработает, поскольку filter
не позволяет изменять значения, которые вы собираетесь возвратить.
В общем, Array.filter
— это удобный и идематический способ работы с массивами, и он отлично подходит для вашей задачи, если вы следите за тем, чтобы изменять именно объекты.
Как перебрать или перечислить объекты в JavaScript?
Итерация через свойства объекта
JavaScript замыкания внутри циклов — простой практический пример
Цикл по массиву в JavaScript
Как перебрать обычный объект JavaScript с объектами в качестве элементов?