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

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

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 здесь)

Ответы

Ответ 1

Всегда существует простой старый цикл 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() не испортить индексы еще не удаленных элементов.

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

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

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

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

Ответ 2

Не in-place, но может выполняться с помощью grep и inArray функций jQuery.

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

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

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

Ответ 3

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

while(IndexesToBeRemoved.length) {
    elements.splice(IndexesToBeRemoved.pop(), 1);
}

Ответ 4

Я предлагаю вам использовать 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;
})

Ответ 5

function filtermethod(element, index, array) {  
    return removeValFromIndex.find(index)
}  
var result = valuesArr.filter(filtermethod);

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

Ответ 6

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

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

Ответ 7

Кажется необходимым отправить ответ с O(n) time:). Проблема с решением сплайсинга состоит в том, что из-за того, что основная реализация массива буквально массива, каждый вызов 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 это различие будет намного больше.

Ответ 8

Простое решение с использованием 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);
});

Ответ 9

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

Ответ 10

Если вы используете 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"]

Ответ 12

filter + indexOf (IE9 +):

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

Или с ES6 filter + find (Edge +):

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

Ответ 13

Здесь быстро.

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]

Ответ 14

вот немного более оптимизированная версия принятых ответов. для одного мы можем настроить вызов сплайсинга так, чтобы мы удалили несколько последовательных элементов за один вызов. Другая оптимизация - разорвать цикл раньше, если мы удалим все элементы из массива (в typescript):

function removeFromArray(arr: any[], elements: any[]) {
    let deleteCount = 0,
        total = elements.length
    for (let i = arr.length; i--;) {
        if (~elements.indexOf(arr[i])) {
            deleteCount++ // optimize removal of consecutive elements
        } else if (deleteCount) {
            arr.splice(i + 1, deleteCount)
            if ((total -= deleteCount) === 0) { // if we removed all already, break early
                deleteCount = 0
                break
            }
            deleteCount = 0
        }
    }
    if (deleteCount) {
        arr.splice(0, deleteCount)
    }
}

Ответ 15

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

Я предлагаю вам использовать 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;

Ответ 16

Быстрый 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))

Ответ 17

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

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

Ответ 18

Простое и эффективное (линейная сложность) решение с использованием фильтров и 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);

Ответ 19

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

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

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

Ответ 20

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

Ответ 21

Похоже, Apply может быть тем, что вы ищете.
может, что-то вроде этого будет работать?

Array.prototype.splice.apply(valuesArray, removeValFromIndexes );