Видаліть кілька елементів з масиву в Javascript / jQuery


117

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

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

EDIT : Щойно побачив ваш коментар до іншої відповіді про масив індексів, не обов'язково в якомусь конкретному порядку. Якщо це так, просто відсортуйте його у порядку зменшення перед початком роботи:

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

І дотримуйтесь цього за допомогою будь-якого $.each()способу циклічного використання // тощо.


1
звичайне нарізання псують індекс
Мухаммед Умер

5
@MuhammadUmer - Ні, не, якщо ви зробите це правильно, саме це пояснює мою відповідь.
nnnnnn

5
Дякуємо за усвідомлення зворотного порядку.
Даніель Налбах

2
+1, я не зрозумів, що треба робити сплайс у зворотному порядку, хоча замість використання forEach використовується мій підхід$.each(rvm.reverse(), function(e, i ) {})
Луїс Стенлі Джовел

1
це буде працювати лише в тому випадку, якщо removeValFromIndex відсортовано у порядку зростання
Kunal Burangi,

24

Ось такий, який я використовую, коли не збираюся позначити / підкреслити:

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

Елегантне рішення! Спочатку я подумав, що це не спрацює, бо думав, що кожного разу, коли ви телефонуєте, sliceвам доведеться перерахувати індекси, які потрібно видалити (-1 на IndexestoBeRemoved), але це насправді працює!
Ренато Гама

2
Занадто багато розумних
Farzad YZ

11
Це рішення працює, якщо лише IndexesToBeRemovedмасив відсортований за зростанням.
xfg

Ці індекси будуть визнані недійсними після першого сплаву.
shinzou

@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

перевірити цю загадку.


Це було б (досить близько) до місця, якби ви щойно сказалиvaluesArr = $.grep(...);
nnnnnn

1
@nnnnnn ha ha ha Я їхав на зустріч (ви знаєте, що вони роблять вас такими продуктивними), тому не експериментував багато.
TheVillageIdiot

1
@ cept0 Чому потік? ОП попросило рішення jQuery.
Ste77

Дуже дякую! @TheVillageIdiot
ecorvo

jsfiddler - Помилка 404. Нам справді шкода, але такої сторінки немає.
Еш

17

Я пропоную вам використовувати 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;
})

індексЗ фільтра ... не оптимально
Альваро Жоао,

1
піднести швидше, ніж метод сплайсингу згідно jsperf.com/remove-multiple/1
lionbigcat

Чудово! Тепер я можу спати :)
Firmansyah

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

MDN посилання тут


@ riship89 Загадка зникла
mate64

@Karna: fiddle is down
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ця різниця буде набагато більшою.


5

Швидкий 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))

Ваш код повинен працювати швидше , якщо ви зробите і використовувати замість . removeValFromIndexSet()removeValFromIndex.hasincludes
Борис

5

Просте та ефективне (лінійна складність) рішення з використанням фільтра та набору :

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функція) пошуку пошуку займає постійний час, швидше, ніж, наприклад, відповідь невади.


@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 }).


Мені добре виглядає - 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"]


Як міститься, реалізовано в підкресленні ... будьте обережні, якщо він еквівалентний індексуЗсередини фільтра ... зовсім не оптимальним ...
Альваро Жоао


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 всередині фільтра ... не оптимально
Alvaro Joao

0

Звучить, як застосувати, може бути те, що ви шукаєте.
можливо, щось подібне спрацювало б?

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

Але .splice()метод не очікує видалення списку елементів, він очікує єдиного індексу елемента, з якого слід почати видалення з наступним числом елементів для видалення ...
nnnnnn

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

Ви можете сконструювати a Setз масиву, а потім створити масив із набору.

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.