Подтвердить что ты не робот

Объединение массива Javascript из обратного вызова, переданного в forEach

У меня есть этот код, который должен перебирать каждый элемент в массиве, удаляя элементы на основе некоторого условия:

//iterate over all items in an array
//if the item is "b", remove it.

var array = ["a", "b", "c"];

array.forEach(function(item) {
    if(item === "b") {
        array.splice(array.indexOf(item), 1);
    }

    console.log(item);
});

Требуемый вывод:

a
b
c

Фактический выход:

a
b

Очевидно, что метод native forEach не проверяет после каждой итерации, был ли элемент удален, поэтому, если это то, то следующий элемент пропускается. Есть ли лучший способ сделать это, помимо переопределения метода forEach или реализации моего собственного класса для использования вместо массива?

Изменить - в дополнение к моему комментарию, я полагаю, что решение состоит в том, чтобы просто использовать стандарт для цикла. Не стесняйтесь отвечать, если у вас есть лучший способ.

4b9b3361

Ответ 1

Давайте посмотрим, почему JavaScript ведет себя так. Согласно стандартной спецификации ECMAScript для Array.prototype.forEach,

когда вы удаляете элемент с индексом 1, элемент с индексом 2 становится элементом с индексом 1, а индекс 2 для этого объекта не существует.

Теперь JavaScript ищет элемент 2 в объекте, который не найден, поэтому он пропускает вызов функции.

Вот почему вы видите только a и b.


Фактический способ сделать это, это использовать 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' ]

Ответ 2

Одна из возможностей заключается в использовании функции array.slice(0), которая создает копию массива (clone), и, следовательно, итерация отделена от удаления,

Тогда единственным изменением первоначального подхода с использованием array.forEach было бы изменение его на array.slice(0).forEach, и оно будет работать:

array.slice(0).forEach(function(item) {
    if(item === "b") {
        array.splice(array.indexOf(item), 1);
    }
    alert(item)
});

После forEach массив будет содержать только a и b.

A jsFiddle demo можно найти здесь.

Ответ 3

Все вышеупомянутые ответы просто терпят неудачу или не сохраняют исходный массив для передачи в другое место, если мы должны были удалить два элемента по определенному индексу и продолжить итерацию от непосредственного элемента. предположим, у меня есть массив

vehicles = [{make: ford, model: mustang}, 
            {make: chevy, model: camaro}, 
            {make: chevy, model: camaro},
            {make: ford, model: mustang},
            {make: chevy, model: camaro}]

Я хочу разделить два элемента, если есть последовательное сочетание Ford и Chevy.

vehicles.forEach(function (vehicle) {
         if (vehicle) {
              var index = vehicles.indexOf(vehicle);
              var flag = vehicle.make=== "ford" && vehicles[index + 1].make=== "chevy";
              if (flag) {
                  //Array.Prototype.forEach() wouldn't update the iteration index after splice
                  vehicles.splice(index, 2, null);
              }
          }
});

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

//After all the iteration is done, we clear all the inserted null
vehicles = [].concat(vehicles.filter(Boolean));

Это может быть лучшим способом ничего не мешать и безошибочно решить это жуткое поведение javascript.

Ответ 4

Другая возможность - использовать функцию array.reduceRight чтобы избежать пропуска:

//iterate over all items in an array from right to left
//if the item is "b", remove it.

const array = ["a", "b", "c"];

array.reduceRight((_, item, i) => {
    if(item === "b") {
        array.splice(i, 1);
    }

});

console.log(array);

После reduceRight массив будет содержать только a и c.