Удаление нескольких элементов из массива в 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 здесь)

526   19  

19 ответов:

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

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

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

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

не in-place но можно сделать с помощью grep и inArray функции jQuery.

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

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

Регистрация этой скрипка.

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

while(IndexesToBeRemoved.length) {
    elements.splice(IndexesToBeRemoved.pop(), 1);
}
function filtermethod(element, index, array) {  
    return removeValFromIndex.find(index)
}  
var result = valuesArr.filter(filtermethod);

ссылка MDN-это здесь

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

var valuesArr = ["v1","v2","v3","v4","v5"];
var removeValFrom = [0, 2, 4];
valuesArr = valuesArr.filter(function(value, index) {
     return removeValFrom.indexOf(index) == -1;
})

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

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

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

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

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

вот один вариант:

valuesArr = removeValFromIndex.reduceRight(function (arr, it) {
    arr.splice(it, 1);
    return arr;
}, valuesArr.sort(function (a, b) { return b - a }));

пример на jsFiddle

MDN на массиве.прототип.reduceRight

если вы используете подчеркивания.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"]

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

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

или с ES6 фильтр + найти (край+):

function removeMany(array, indexes = []) {
  return array.filter((_, idx) => indexes.indexOf(idx) === -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]

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

для нескольких элементов или уникальный элемент:

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

никогда не используйте 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

Do:

с хэшами... используя массив.прототип.карта

  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;

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

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

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

звучит как применить может быть то, что вы ищете.
может быть, что-то вроде этого сработает?

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

Comments

    Ничего не найдено.