Loop для удаления элемента в массиве с несколькими вхождениями

Я хочу удалить элемент в массиве с несколькими вхождениями с помощью функции.

var array=["hello","hello","world",1,"world"];

function removeItem(item){
    for(i in array){
        if(array[i]==item) array.splice(i,1);
    }
}
removeItem("world");
//Return hello,hello,1
removeItem("hello");
//Return hello,world,1,world

Этот цикл не удаляет элемент, когда он повторяется дважды в последовательности, удаляет только один из них.

Почему?

Ответ 1

У вас есть встроенная функция filter которая фильтрует массив на основе предиката (условия).

Он не изменяет исходный массив, но возвращает новый отфильтрованный.

var array=["hello","hello","world",1,"world"];
var filtered = array.filter(function(element) {
    return element !== "hello";
}); // filtered contains no occurrences of hello

Вы можете извлечь его в функцию:

function without(array, what){
    return array.filter(function(element){ 
        return element !== what;
    });
}

Однако оригинальный фильтр кажется достаточно выразительным.

Вот ссылка на его документацию

Ваша оригинальная функция имеет несколько проблем:

  • Он выполняет итерацию массива с использованием цикла for... in который не гарантирует порядок итераций. Кроме того, не используйте его для перебора массивов - предпочитайте нормальный цикл for... или .forEach
  • Вы выполняете итерацию массива с ошибкой "один за другим", поэтому пропускаете следующий элемент, поскольку вы одновременно удаляете элемент и прогрессируете массив.

Ответ 2

Это потому, что for -loop переходит к следующему элементу после удаления события, тем самым пропуская элемент сразу после этого.

Например, допустим, что в этом массиве необходимо удалить item1 (обратите внимание, что <- является индексом цикла):

item1 (<-), item2, item3

после удаления:

item2 (<-), item3

и после обновления индекса (по завершении цикла)

item2, item3 (<-)

Итак, вы можете видеть, что item2 пропущен и, следовательно, не проверен!

Поэтому вам нужно будет компенсировать это, уменьшив индекс вручную на 1, как показано ниже:

function removeItem(item){
    for(var i = 0; i < array.length; i++){
        if(array[i]==item) {
            array.splice(i,1);
            i--; // Prevent skipping an item
        }
    }
}

Вместо использования этого for -loop вы можете использовать более "современные" методы для filter из нежелательных элементов, как показано в другом ответе Бенджамина.

Ответ 3

Ни один из этих ответов не является оптимальным. Принятый ответ с фильтром приведет к созданию нового экземпляра массива. Ответ со вторым большинством голосов, цикл for, который делает шаг назад на каждом сращивании, излишне сложный.

Если вы хотите сделать подход петли цикла for, просто пересчитайте назад до 0.

for (var i = array.length - 0; i >= 0; i--) {
  if (array[i] === item) {
    array.splice(i, 1);
  }
}

Однако, я использовал неожиданно быстрый метод с циклом while и indexOf:

var itemIndex = 0;
while ((itemIndex = valuesArray.indexOf(findItem, itemIndex)) > -1) {
  valuesArray.splice(itemIndex, 1);
}

Что делает этот метод не повторяющимся, так это то, что после любого удаления следующий поиск начинается с индекса следующего элемента после удаленного элемента. Это потому, что вы можете передать начальный индекс в indexOf в качестве второго параметра.

В тестовом примере jsPerf, сравнивая два вышеуказанных метода и принятый метод фильтрации, indexOf обычно закончил сначала на Firefox и Chrome, и был вторым по IE. Метод filter всегда был медленнее с большим отрывом.

Вывод: Либо обратный для цикла while, а indexOf - лучшие методы, которые можно найти для удаления нескольких экземпляров одного и того же элемента из массива. Использование фильтра создает новый массив и работает медленнее, поэтому я бы избегал этого.

Ответ 4

В этом случае вы можете использовать loadash или подчеркивание js Если arr - массив, вы можете удалить дубликаты:

var arr = [2,3,4,4,5,5];

arr = _.uniq(arr);

Ответ 5

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

удаление "привет" "Начать цикл. я = 0, array = [" hello "," hello "," world ", 1," world"] я указывает на "привет"   удалить первый элемент, я = 0 array = ["hello "," world ", 1," world"] следующий цикл, я = 1, array = ["hello "," world ", 1," world"]. второе "привет" не будет удалено.

Давайте посмотрим на "мир" = i = 2, указывает на "мир" (удалить). в следующем цикле массив: [ "привет", "привет", 1, "мир" ] и я = 3. здесь прошел второй "мир".

что вы хотите? вы хотите удалить все экземпляры элемента? или только первый? для первого случая удаление должно быть в

while (array[i] == item) array.splice(i,1);

для второго случая - верните, как только вы удалили элемент.

Ответ 6

Создайте набор, заданный для массива, исходный массив немодифицирован

Демо на скрипте

    var array=["hello","hello","world",1,"world"];

    function removeDups(items) {
        var i,
            setObj = {},
            setArray = [];
        for (i = 0; i < items.length; i += 1) {
            if (!setObj.hasOwnProperty(items[i])) {
                setArray.push(items[i]);
                setObj[items[i]] = true;
            }
        }
        return setArray;
    }

    console.log(removeDups(array)); // ["hello", "world", 1]

Ответ 7

Я должен сказать, что мой подход не использует функцию splice, и вам нужен еще один массив для этого решения.

Прежде всего, я предполагаю, что ваш способ петли массива неверен. Вы используете циклы for in, которые предназначены для объектов, а не для массивов. Лучше использовать $.each, если вы используете jQuery или Array.prototype.forEach, если используете vanila Javascript.

Во-вторых, почему бы не создать новый пустой массив, зациклившись на нем и добавив только уникальные элементы в новый массив, например:

ПЕРВЫЙ ПОДХОД (jQuery):

 var newArray = [];
 $.each(array, function(i, element) {
        if ($.inArray(element, newArray) === -1) {
            newArray.push(region);
        }
 });

ВТОРОЙ ПОДХОД (Ванила Javascript):

var newArray = [];
array.forEach(function(i, element) {
  if (newArray.indexOf(element) === -1) {
            newArray.push(region);
  }
});

Ответ 8

Мне нужно небольшое изменение этого, возможность удалить "n" вхождения элемента из массива, поэтому я изменил @Veger как:

function removeArrayItemNTimes(arr,toRemove,times){
    times = times || 10;
    for(var i = 0; i < arr.length; i++){
        if(arr[i]==toRemove) {
            arr.splice(i,1);
            i--; // Prevent skipping an item
            times--;
            if (times<=0) break;
        }
    }
    return arr;
}

Ответ 9

Альтернативный подход заключается в сортировке массива и последующем воспроизведении индексов значений.

function(arr) {
    var sortedArray = arr.sort();
    //In case of numbers, you can use arr.sort(function(a,b) {return a - b;})
    for (var i = 0; sortedArray.length; i++) {
        if (sortedArray.indexOf(sortedArray[i]) === sortedArray.lastIndexOf(sortedArray[i]))
            continue;
        else
            sortedArray.splice(sortedArray.indexOf(sortedArray[i]), (sortedArray.lastIndexOf(sortedArray[i]) - sortedArray.indexOf(sortedArray[i])));
    }
}