Цикл для удаления элемента в массиве 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])));
}
}