Удаление нескольких элементов из массива в 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"]
Ответ 11
Здесь одна возможность:
valuesArr = removeValFromIndex.reduceRight(function (arr, it) {
arr.splice(it, 1);
return arr;
}, valuesArr.sort(function (a, b) { return b - a }));
Пример jsFiddle
MDN на Array.prototype.reduceRight
Ответ 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 );