6

Обход массива и удаление элементов без нарушения цикла for

1

Я столкнулся с проблемой в следующем цикле 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 ответ(ов)

10

Массив переиндексируется, когда вы используете метод .splice(), что приводит к пропуску индексов при удалении элемента, и ваше кэшированное значение .length становится устаревшим.

Чтобы исправить это, вам нужно либо уменьшать i после вызова .splice(), либо просто итераровать в обратном порядке...

var i = Auction.auctions.length;
while (i--) {
    ...
    if (...) { 
        Auction.auctions.splice(i, 1);
    } 
}

Таким образом, переиндексирование не повлияет на следующий элемент в итерации, поскольку изменение индексов затрагивает только элементы от текущей позиции до конца массива, а следующий элемент в итерации будет иметь индекс ниже текущей позиции.

2

Это довольно распространенная проблема. Решение заключается в том, чтобы перебирать элементы в обратном порядке:

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

Не имеет значения, если вы удаляете элементы с конца массива, потому что индексы сохранятся при проходе в обратном порядке.

0

Вместо того чтобы рассчитывать длину массива только в начале цикла, вы можете перерасчитывать ее на каждой итерации. Например:

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.

0

Данный фрагмент кода используется для фильтрации массива аукционов Auction.auctions. Он проверяет, осталось ли время (в секундах) для каждого аукциона. Если значение el["seconds"], соответствующее каждому аукциону, больше нуля после уменьшения на единицу, то аукцион остается в массиве. В противном случае он удаляется.

Вот как это работает:

  1. Метод filter создает новый массив, включающий только те элементы, для которых переданная функция возвращает true.
  2. Внутри функции мы уменьшаем значение seconds на 1 с помощью postfix операции --, затем проверяем, больше ли оно нуля.
  3. Если seconds больше нуля, значит аукцион все еще активен, и он останется в новом массиве.

Таким образом, код эффективно удаляет все аукционы, у которых время истекло.

0

Если вы используете ES6 и выше, то почему бы не воспользоваться методом Array.filter? Этот метод позволяет вам легко фильтровать массивы. В вашем случае можно сделать следующее:

Auction.auctions = Auction.auctions.filter((auction) => {
  auction['seconds']--; // Уменьшаем значение секунда
  return auction.seconds > 0; // Фильтруем по условию
});

Обратите внимание, что изменение элемента массива во время итерации с помощью filter будет работать только для объектов, как в вашем примере. Если вы работаете с массивом примитивных значений (например, числами или строками), то это не сработает, поскольку filter не позволяет изменять значения, которые вы собираетесь возвратить.

В общем, Array.filter — это удобный и идематический способ работы с массивами, и он отлично подходит для вашей задачи, если вы следите за тем, чтобы изменять именно объекты.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь