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

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

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

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

почему?

9 ответов


у вас есть встроенная функция под названием 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
  • вы повторяете массив с ошибкой off-by-one, поэтому вы пропускаете следующий элемент, так как вы удаляете элемент и прогрессируете массив.

это потому, что 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-петли, вы можете использовать более "современные" методы фильтр из нежелательных элементов, как показано в другой ответ Бенджамина.


ни один из этих ответов не является очень оптимальным. Принятый ответ с фильтром приведет к созданию нового экземпляра массива. Ответ со вторым большинством голосов, цикл 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. The filter метод всегда был медленнее широким маржа.

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


в этом случае вы можете использовать loadash или underscore js если arr является массивом, вы можете удалить дубликаты с помощью:

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

arr = _.uniq(arr);

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

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

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

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

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

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


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

демо на скрипке

    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]

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

прежде всего, я думаю, что ваш способ зацикливания массива не является правильным. Вы используете 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);
        }
 });

второй подход (Vanila Javascript):

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

Мне нужно было небольшое изменение этого, возможность удаления " 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;
}

альтернативным подходом было бы сортировать массив, а затем играть с индексами значений.

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