Usuń wiele elementów z tablicy w Javascript / jQuery

118

Mam dwie tablice. Pierwsza tablica zawiera jakieś wartości, podczas gdy druga tablica zawiera indeksy wartości, które powinny zostać usunięte z pierwszej tablicy. Na przykład:

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

Chcę usunąć wartości obecne w indeksach 0,2,4z valuesArr. Pomyślałem, że splicemetoda rodzima może pomóc, więc wymyśliłem:

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

Ale to nie zadziałało, ponieważ po każdym spliceindeksy wartości w valuesArrbyły różne. Mógłbym rozwiązać ten problem, używając tymczasowej tablicy i kopiując wszystkie wartości do drugiej tablicy, ale zastanawiałem się, czy istnieją jakieś natywne metody, do których możemy przekazać wiele indeksów, w których można usunąć wartości z tablicy.

Wolałbym rozwiązanie jQuery. (Nie jestem pewien, czy mogę użyć greptutaj)

xyz
źródło

Odpowiedzi:

256

Zawsze istnieje zwykła stara forpętla:

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

Przejdź removeValFromIndexw odwrotnej kolejności i możesz.splice() bez zepsucia indeksów elementów, które jeszcze nie zostały usunięte.

Uwaga w powyższym przypadku użyłem składni literału tablicy z nawiasami kwadratowymi, aby zadeklarować dwie tablice. Jest to zalecana składnia, ponieważnew Array() użycie może być mylące, biorąc pod uwagę, że reaguje inaczej w zależności od liczby przekazanych parametrów.

EDYCJA : Właśnie zobaczyłem Twój komentarz dotyczący innej odpowiedzi na temat tablicy indeksów niekoniecznie w określonej kolejności. Jeśli tak jest, po prostu posortuj to w porządku malejącym przed rozpoczęciem:

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

I postępuj zgodnie z tą dowolną $.each()metodą zapętlania / / itp.

nnnnnn
źródło
1
nie zepsuje indeksu
Muhammad Umer
5
@MuhammadUmer - Nie, nie, jeśli zrobisz to poprawnie, co wyjaśnia moja odpowiedź.
nnnnnn
5
Dzięki za świadomość odwrotnej kolejności.
Daniel Nalbach
2
+1, nie zdawałem sobie sprawy, że muszę wykonać połączenie w odwrotnej kolejności, chociaż zamiast forEach, moje podejście opiera się na$.each(rvm.reverse(), function(e, i ) {})
Luis Stanley Jovel
1
zadziała to tylko wtedy, gdy removeValFromIndex zostanie posortowane w kolejności rosnącej
Kunal Burangi,
24

Oto jeden, którego używam, gdy nie wybieram lodash / underscore:

while(IndexesToBeRemoved.length) {
    elements.splice(IndexesToBeRemoved.pop(), 1);
}
Dan Ochiana
źródło
Eleganckie rozwiązanie! Na początku myślałem, że to nie zadziała, ponieważ myślałem, że za każdym razem, gdy dzwonisz, slicebędziesz musiał ponownie obliczyć indeksy do usunięcia (-1 z IndexestoBeRemoved), ale to faktycznie działa!
Renato Gama
2
Za dużo sprytny
Farzad YZ
11
To rozwiązanie działa, jeśli tylko IndexesToBeRemovedtablica jest sortowana rosnąco.
xfg
Indeksy te zostaną unieważnione po pierwszym połączeniu.
shinzou,
@shinzou - nie, jeśli IndexesToBeRemovedjest posortowane (rosnąco).
nnnnnn
18

Nie, in-placeale można to zrobić za pomocą grepi inArrayfunkcji jQuery.

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

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

sprawdź to skrzypce.

TheVillageIdiot
źródło
Byłoby (wystarczająco blisko) na miejscu, gdybyś powiedziałvaluesArr = $.grep(...);
nnnnnn
1
@nnnnnn ha ha ha Wyjeżdżałem na spotkanie (wiesz, dzięki nim jesteś tak produktywny), więc niewiele eksperymentowałem.
TheVillageIdiot
1
@ cept0 Dlaczego głos przeciw? OP poprosił o rozwiązanie jQuery.
Ste77
Dziękuję bardzo! @TheVillageIdiot
ecorvo
jsfiddler - Błąd 404. Bardzo nam przykro, ale nie ma takiej strony.
Ash,
18

Sugeruję użycie 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;
})
Саша Давиденко
źródło
indeksWewnątrz filtra ... nieoptymalne
Alvaro Joao
1
głosować za. szybciej niż metoda łączenia według jsperf.com/remove-multiple/1
lionbigcat
Wspaniały! Teraz mogę spać :)
Firmansyah
7
function filtermethod(element, index, array) {  
    return removeValFromIndex.find(index)
}  
var result = valuesArr.filter(filtermethod);

Odniesienie do MDN jest tutaj

riship89
źródło
@ riship89 Skrzypce nie działają
mate64
@Karna: Fiddle is down
riship89
1
Należy pamiętać, że w chwili pisania tego tekstu (czerwiec 2014 r.) Array.prototype.find jest częścią bieżącej wersji roboczej ES6 i zaimplementowane tylko w bieżącym Firefoksie
Olli K
6

W czystym JS możesz zapętlić tablicę od tyłu, więc splice()nie zepsujesz indeksów kolejnych elementów w pętli:

for (var i = arr.length - 1; i >= 0; i--) {
    if ( yuck(arr[i]) ) {
        arr.splice(i, 1);
    }
}
Watchduck
źródło
Nie działa fuj nie jest funkcją, więc założyłem, że jest to indeks niechcianych indeksów elementów i używane fuj [arr [i]]
DavChana
5

Z O(n)czasem trzeba odpowiedzieć :). Problem z rozwiązaniem splice polega na tym, że ze względu na to, że podstawowa implementacja tablicy jest dosłownie tablicą , każde splicewywołanie zajmie trochę O(n)czasu. Jest to najbardziej widoczne, gdy ustawiamy przykład, aby wykorzystać to zachowanie:

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)

To usuwa elementy od środka do początku, stąd każde usunięcie wymusza na silniku js skopiowanie n/2elementów, mamy (n/2)^2w sumie operacje kopiowania, które są kwadratowe.

Rozwiązanie łączenia (zakładając, że isjest już posortowane w kolejności malejącej, aby pozbyć się narzutów) wygląda następująco:

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

Jednak nie jest trudno zaimplementować liniowe rozwiązanie czasu, rekonstruując tablicę od zera, używając maski, aby zobaczyć, czy kopiujemy elementy, czy nie (sortowanie przesunie to O(n)log(n)). Oto taka implementacja (nie maskjest to logiczna inwersja dla prędkości):

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

Uruchomiłem to na jsperf.com i nawet n=100metoda łączenia jest o pełne 90% wolniejsza. W przypadku większych nróżnica ta będzie znacznie większa.

simonzack
źródło
5

Quick ES6 jedna wkładka:

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
źródło
Twój kod powinien działać szybciej, jeśli utworzysz removeValFromIndexa Set()i użyjesz removeValFromIndex.haszamiast includes.
Boris
5

Proste i wydajne (liniowa złożoność) rozwiązanie wykorzystujące filtr i zestaw :

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

Ogromną zaletą tej implementacji jest to, że operacja wyszukiwania Set ( hasfunkcja) zajmuje stały czas, na przykład jest szybsza niż odpowiedź Nevace'a.

Alberto Trindade Tavares
źródło
@MichaelPaccione Glad to help :)
Alberto Trindade Tavares
3

Działa to dobrze dla mnie i działa również podczas usuwania z tablicy obiektów:

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

Może istnieć krótszy, bardziej efektywny sposób napisania tego, ale to działa.

StuartMc
źródło
2

Proste rozwiązanie wykorzystujące ES5. Wydaje się to bardziej odpowiednie dla większości dzisiejszych aplikacji, ponieważ wielu nie chce już polegać na jQuery itp.

Kiedy indeksy do usunięcia są posortowane w kolejności rosnącej:

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

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

Gdy indeksy do usunięcia nie są posortowane:

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);
});
Kaspar Fenner
źródło
1

Możesz poprawić swój kod, zastępując removeValFromIndexgo removeValFromIndex.reverse(). Jeśli nie ma gwarancji, że ta tablica będzie używać kolejności rosnącej, możesz zamiast tego użyć removeValFromIndex.sort(function(a, b) { return b - a }).

minopret
źródło
Dla mnie wygląda dobrze - jsfiddle.net/mrtsherman/gDcFu/2 . Chociaż pozwala to przypuszczać, że lista usunięcia jest w porządku.
mrtsherman
@minopret: Dzięki, ale będzie działać tylko wtedy, gdy indeksy w removeValFromIndexsą w porządku rosnącym.
xyz,
1

Jeśli używasz underscore.js , możesz użyć _.filter()do rozwiązania problemu.

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

Dodatkowo, jeśli próbujesz usunąć elementy za pomocą listy elementów zamiast indeksów, możesz po prostu użyć _.without():

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

Teraz filteredArrpowinno["V2", "V4", "V5"]

Johnny Zhao
źródło
Jak zawiera realizowany jest w podkreślenia ... być ostrożnym, jeśli jest to równoznaczne z indexOf wewnątrz filtra ... nie optimum w ogóle ...
Alvaro Joao
1

filter + indexOf (IE9 +):

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

Lub z filtrem ES6 + znajdź (Edge +):

function removeMany(array, indexes = []) {
  return array.filter((_, idx) => indexes.indexOf(idx) === -1)
}
daviestar
źródło
indeksWewnątrz filtra ... nieoptymalne
Alvaro Joao
1

Oto szybki numerek.

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]
Merrick Kavolsky
źródło
indeksWewnątrz filtra ... nieoptymalne
Alvaro Joao
0

Wygląda na to, że aplikacja Apply może być tym, czego szukasz.
może coś takiego zadziała?

Array.prototype.splice.apply(valuesArray, removeValFromIndexes );
Obrabować
źródło
Ale .splice()metoda nie oczekuje listy elementów do usunięcia, oczekuje pojedynczego indeksu elementu, od którego rozpocznie się usuwanie, a następnie liczby elementów do usunięcia ...
nnnnnn
0

W przypadku wielu przedmiotów lub unikatowego przedmiotu:

Sugeruję użycie Array.prototype.filter

Nigdy nie używaj indexOf, jeśli już znasz ten indeks !:

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

Robić:

with Hashes ... using 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;
Alvaro Joao
źródło
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);

To działa. Jednak w trakcie tego procesu utworzyłbyś nową tablicę. Nie jestem pewien, czy tego chcesz, czy nie, ale technicznie byłoby to tablica zawierająca tylko żądane wartości.


źródło
-1

Możesz spróbować użyć delete array[index]To nie usunie całkowicie elementu, ale raczej ustawi wartość na undefined.

Henesnarfel
źródło
Dzięki, ale chcę usunąć elementy.
xyz,
-1

Możesz skonstruować Settablicę z tablicy, a następnie utworzyć tablicę z zestawu.

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]
Mohib
źródło