Удалить несколько элементов из массива в Javascript / jQuery

118

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

var valuesArr = new Array("v1","v2","v3","v4","v5");   
var removeValFromIndex = new Array(0,2,4);

Я хочу удалить значения, присутствующие в индексах 0,2,4из valuesArr. Я подумал, что собственный spliceметод может помочь, поэтому я придумал:

$.each(removeValFromIndex,function(index,value){
    valuesArr.splice(value,1);
});

Но это не сработало, потому что после каждого spliceиндексы значений valuesArrбыли разными. Я мог бы решить эту проблему, используя временный массив и скопировав все значения во второй массив, но мне было интересно, есть ли какие-нибудь собственные методы, которым мы можем передать несколько индексов, по которым можно удалить значения из массива.

Я бы предпочел решение jQuery. (Не уверен, что могу использовать grepздесь)

хуг
источник

Ответы:

256

Всегда есть простой старый forцикл:

var valuesArr = ["v1","v2","v3","v4","v5"],
    removeValFromIndex = [0,2,4];    

for (var i = removeValFromIndex.length -1; i >= 0; i--)
   valuesArr.splice(removeValFromIndex[i],1);

Пройдите removeValFromIndexв обратном порядке, и вы сможете .splice()не испортить индексы еще не удаленных элементов.

Обратите внимание, что выше я использовал синтаксис литерала массива с квадратными скобками для объявления двух массивов. Это рекомендуемый синтаксис, поскольку его new Array()использование может сбивать с толку, поскольку он реагирует по-разному в зависимости от того, сколько параметров вы передаете.

РЕДАКТИРОВАТЬ : только что увидел ваш комментарий к другому ответу о массиве индексов, не обязательно в каком-либо конкретном порядке. Если это так, просто отсортируйте его по убыванию, прежде чем начать:

removeValFromIndex.sort(function(a,b){ return b - a; });

И следуйте за этим любым $.each()способом, который вам нравится.

NNNNNN
источник
1
не буду вырезать беспорядок в индексе
Мухаммад Умер
5
@MuhammadUmer - Нет, если вы все делаете правильно, это объясняет мой ответ.
nnnnnn
5
Спасибо за осведомленность об обратном порядке.
Даниэль Нальбах
2
+1, я не понимал, что мне нужно было выполнить склейку в обратном порядке, хотя вместо использования forEach мой подход использует$.each(rvm.reverse(), function(e, i ) {})
Луис Стэнли Джовел
1
это будет работать только в том случае, если оно removeValFromIndex будет отсортировано в порядке возрастания
Кунал Буранги
24

Вот тот, который я использую, когда не использую lodash / подчеркивание:

while(IndexesToBeRemoved.length) {
    elements.splice(IndexesToBeRemoved.pop(), 1);
}
Дэн Очиана
источник
Шикарное решение! Сначала я думал, что это не сработает, потому что я думал, что каждый раз, когда вы звоните, sliceвам придется пересчитывать индексы, которые нужно удалить (-1 IndexestoBeRemoved), но это действительно работает!
Ренато Гама
2
Слишком много умно
Farzad YZ
11
Это решение работает, если только IndexesToBeRemovedмассив отсортирован по возрастанию.
xfg
Эти индексы станут недействительными после первого соединения.
shinzou 03
@shinzou - Нет, если IndexesToBeRemovedотсортировано (по возрастанию).
nnnnnn
18

Нет, in-placeно можно использовать grepи inArrayфункции jQuery.

var arr = $.grep(valuesArr, function(n, i) {
    return $.inArray(i, removeValFromIndex) ==-1;
});

alert(arr);//arr contains V2, V4

проверьте эту скрипку.

TheVillageIdiot
источник
Это было бы (достаточно близко к) на месте, если бы вы только что сказалиvaluesArr = $.grep(...);
nnnnnn
1
@nnnnnn ха-ха-ха Я уезжал на встречу (ты знаешь, они делают тебя такой продуктивной), поэтому особо не экспериментировал.
TheVillageIdiot
1
@ cept0 Почему голос против? OP запросил решение jQuery.
Ste77
Спасибо вам большое! @TheVillageIdiot
ecorvo
jsfiddler - Ошибка 404. К сожалению, такой страницы нет.
Эш
18

Я предлагаю вам использовать Array.prototype.filter

var valuesArr = ["v1","v2","v3","v4","v5"];
var removeValFrom = [0, 2, 4];
valuesArr = valuesArr.filter(function(value, index) {
     return removeValFrom.indexOf(index) == -1;
})
Саша Давиденко
источник
indexOf внутри фильтра ... не оптимально
Альваро Жоао
1
проголосовать. быстрее, чем метод сращивания, согласно jsperf.com/remove-multiple/1
lionbigcat
Большой! Теперь я могу спать :)
Фирмансях
7
function filtermethod(element, index, array) {  
    return removeValFromIndex.find(index)
}  
var result = valuesArr.filter(filtermethod);

Ссылка MDN здесь

ришип89
источник
@ riship89 Скрипка не работает
mate64 04
@Karna: скрипка не работает
riship89
1
Обратите внимание, что на момент написания (июнь 2014 г.) Array.prototype.find является частью текущего проекта ES6 и реализован только в текущем Firefox
Olli K,
6

В чистом JS вы можете перебирать массив в обратном направлении, чтобы splice()не испортить индексы элементов, следующих в цикле:

for (var i = arr.length - 1; i >= 0; i--) {
    if ( yuck(arr[i]) ) {
        arr.splice(i, 1);
    }
}
Сторожевой утка
источник
Не работает yuck - это не функция, поэтому предполагается, что это индекс индексов нежелательных элементов, и используется yuck [arr [i]]
DavChana
5

Считаю необходимым дать ответ со O(n)временем :). Проблема со сращиванием заключается в том, что из-за базовой реализации массива, который буквально является массивом , каждыйsplice вызов будет занимать O(n)время. Это наиболее ярко проявляется, когда мы настраиваем пример использования этого поведения:

var n = 100
var xs = []
for(var i=0; i<n;i++)
  xs.push(i)
var is = []
for(var i=n/2-1; i>=0;i--)
  is.push(i)

Это удаляет элементы, начиная с середины до начала, поэтому каждое удаление заставляет движок js копировать n/2 элементы, у нас есть (n/2)^2операции копирования в целом, которые являются квадратичными.

Решение для стыковки (при условии is оно уже отсортировано в порядке убывания, чтобы избавиться от накладных расходов) выглядит следующим образом:

for(var i=0; i<is.length; i++)
  xs.splice(is[i], 1)

Однако нетрудно реализовать решение с линейным временем, воссоздав массив с нуля, используя маску, чтобы увидеть, копируем ли мы элементы или нет (сортировка подтолкнет это к O(n)log(n)). Ниже приводится такая реализация (не таmask логическое значение инвертировано для скорости):

var mask = new Array(xs.length)
for(var i=is.length - 1; i>=0; i--)
  mask[is[i]] = true
var offset = 0
for(var i=0; i<xs.length; i++){
  if(mask[i] === undefined){
    xs[offset] = xs[i]
    offset++
  }
}
xs.length = offset

Я запускал это на jsperf.com, и даже n=100метод сращивания работает на 90% медленнее. Для большего nэта разница будет намного больше.

simonzack
источник
5

Quick ES6 с одним вкладышем:

const valuesArr = new Array("v1","v2","v3","v4","v5");   
const removeValFromIndex = new Array(0,2,4);

const arrayWithValuesRemoved = valuesArr.filter((value, i) => removeValFromIndex.includes(i))
Nevace
источник
Ваш код должен работать быстрее , если вы сделаете и использовать вместо . removeValFromIndexSet()removeValFromIndex.hasincludes
Борис
5

Простое и эффективное (линейной сложности) решение с использованием filter и Set :

const valuesArr = ['v1', 'v2', 'v3', 'v4', 'v5'];   
const removeValFromIndex = [0, 2, 4];

const indexSet = new Set(removeValFromIndex);

const arrayWithValuesRemoved = valuesArr.filter((value, i) => !indexSet.has(i));

console.log(arrayWithValuesRemoved);

Большим преимуществом этой реализации является то, что операция ( hasфункция) поиска Set занимает постоянное время, например, быстрее, чем ответ Nevace.

Альберто Триндаде Таварес
источник
@MichaelPaccione Рад помочь :)
Альберто Триндади Таварес
3

Это хорошо работает для меня и работает при удалении из массива объектов:

var array = [ 
    { id: 1, name: 'bob', faveColor: 'blue' }, 
    { id: 2, name: 'jane', faveColor: 'red' }, 
    { id: 3, name: 'sam', faveColor: 'blue' }
];

// remove people that like blue

array.filter(x => x.faveColor === 'blue').forEach(x => array.splice(array.indexOf(x), 1));

Может быть более короткий и эффективный способ написать это, но он работает.

Стюарт Мак
источник
2

Простое решение с использованием ES5. В настоящее время это кажется более подходящим для большинства приложений, поскольку многие больше не хотят полагаться на jQuery и т. Д.

Когда удаляемые индексы отсортированы в порядке возрастания:

var valuesArr = ["v1", "v2", "v3", "v4", "v5"];   
var removeValFromIndex = [0, 2, 4]; // ascending

removeValFromIndex.reverse().forEach(function(index) {
  valuesArr.splice(index, 1);
});

Когда удаляемые индексы не отсортированы:

var valuesArr = ["v1", "v2", "v3", "v4", "v5"];   
var removeValFromIndex = [2, 4, 0];  // unsorted

removeValFromIndex.sort(function(a, b) { return b - a; }).forEach(function(index) {
  valuesArr.splice(index, 1);
});
Каспар Феннер
источник
1

Вы можете исправить свой код, заменив его removeValFromIndexна removeValFromIndex.reverse(). Если не гарантируется, что в этом массиве используется возрастающий порядок, вы можете вместо этого использовать removeValFromIndex.sort(function(a, b) { return b - a }).

Minopret
источник
Мне нравится - jsfiddle.net/mrtsherman/gDcFu/2 . Хотя это делает предположение, что список удаления в порядке.
mrtsherman
@minopret: Спасибо, но это будет работать, только если индексы removeValFromIndexв порядке возрастания.
xyz
1

Если вы используете underscore.js , вы можете использовать его _.filter()для решения своей проблемы.

var valuesArr = new Array("v1","v2","v3","v4","v5");
var removeValFromIndex = new Array(0,2,4);
var filteredArr = _.filter(valuesArr, function(item, index){
                  return !_.contains(removeValFromIndex, index);
                });

Кроме того, если вы пытаетесь удалить элементы, используя список элементов вместо индексов, вы можете просто использовать _.without(), например:

var valuesArr = new Array("v1","v2","v3","v4","v5");
var filteredArr = _.without(valuesArr, "V1", "V3");

Теперь filteredArrдолжно быть["V2", "V4", "V5"]

Джонни Чжао
источник
Как contains реализовано в подчеркивании ... будьте осторожны, если он эквивалентен indexOf внутри фильтра ... совсем не оптимален ...
Альваро Жоао
1

фильтр + indexOf (IE9 +):

function removeMany(array, indexes) {
  return array.filter(function(_, idx) {
    return indexes.indexOf(idx) === -1;
  });
}); 

Или с фильтром ES6 + find (Edge +):

function removeMany(array, indexes = []) {
  return array.filter((_, idx) => indexes.indexOf(idx) === -1)
}
daviestar
источник
indexOf внутри фильтра ... не оптимально
Альваро Жоао
1

Вот быстрый прием.

function removeFromArray(arr, toRemove){
    return arr.filter(item => toRemove.indexOf(item) === -1)
}

const arr1 = [1, 2, 3, 4, 5, 6, 7]
const arr2 = removeFromArray(arr1, [2, 4, 6]) // [1,3,5,7]
Меррик Кавольски
источник
indexOf внутри фильтра ... не оптимально
Альваро Жоао
0

Похоже, что Apply может быть тем, что вы ищете.
может что-то подобное сработает?

Array.prototype.splice.apply(valuesArray, removeValFromIndexes );
Роб
источник
Но .splice()метод не ожидает списка удаляемых элементов, он ожидает единый индекс элемента, с которого нужно начать удаление, за которым следует количество
удаляемых
0

Для нескольких элементов или уникального элемента:

Я предлагаю вам использовать Array.prototype.filter

Никогда не используйте indexOf, если вы уже знаете индекс !:

var valuesArr = ["v1","v2","v3","v4","v5"];
var removeValFrom = [0, 2, 4];

valuesArr = valuesArr.filter(function(value, index) {
     return removeValFrom.indexOf(index) == -1;
}); // BIG O(N*m) where N is length of valuesArr and m is length removeValFrom

Делать:

с хешами ... используя Array.prototype.map

  var valuesArr = ["v1","v2","v3","v4","v5"];
  var removeValFrom = {};
  ([0, 2, 4]).map(x=>removeValFrom[x]=1); //bild the hash.
  valuesArr = valuesArr.filter(function(value, index) {
      return removeValFrom[index] == 1;
  }); // BIG O(N) where N is valuesArr;
Альваро Жоао
источник
0
var valuesArr = new Array("v1","v2","v3","v4","v5");   
var removeValFromIndex = new Array(0,2,4);

console.log(valuesArr)
let arr2 = [];

for (let i = 0; i < valuesArr.length; i++){
  if (    //could also just imput this below instead of index value
    valuesArr[i] !== valuesArr[0] && // "v1" <--
    valuesArr[i] !== valuesArr[2] && // "v3" <--
    valuesArr[i] !== valuesArr[4]    // "v5" <--
  ){
    arr2.push(valuesArr[i]);
  }
}

console.log(arr2);

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


источник
-1

Вы можете попробовать и использовать. delete array[index]Это не удалит элемент полностью, а скорее установит значение undefined.

Хенеснарфель
источник
Спасибо, но я хочу удалить элементы.
xyz
-1

Вы можете построить Setиз массива, а затем создать массив из набора.

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]
Mohib
источник