Supprimer plusieurs éléments du tableau dans Javascript / jQuery

117

J'ai deux tableaux. Le premier tableau contient des valeurs tandis que le second tableau contient des indices des valeurs qui doivent être supprimées du premier tableau. Par exemple:

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

Je souhaite supprimer les valeurs présentes dans les indices 0,2,4de valuesArr. J'ai pensé que la spliceméthode native pourrait aider, alors j'ai proposé:

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

Mais cela n'a pas fonctionné car après chaque splice, les indices des valeurs valuesArrétaient différents. Je pourrais résoudre ce problème en utilisant un tableau temporaire et en copiant toutes les valeurs dans le deuxième tableau, mais je me demandais s'il existe des méthodes natives auxquelles nous pouvons transmettre plusieurs indices auxquels supprimer des valeurs d'un tableau.

Je préférerais une solution jQuery. (Je ne sais pas si je peux utiliser grepici)

xyz
la source

Réponses:

256

Il y a toujours la vieille forboucle simple :

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

Passez removeValFromIndexdans l'ordre inverse et vous pouvez.splice() sans gâcher les index des éléments encore à supprimer.

Notez que dans ce qui précède, j'ai utilisé la syntaxe de tableau littéral avec des crochets pour déclarer les deux tableaux. Il s'agit de la syntaxe recommandée car l' new Array()utilisation est potentiellement déroutante étant donné qu'elle répond différemment en fonction du nombre de paramètres que vous transmettez.

EDIT : Je viens de voir votre commentaire sur une autre réponse sur le tableau d'index pas nécessairement dans un ordre particulier. Si tel est le cas, triez-le simplement par ordre décroissant avant de commencer:

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

Et suivez cela avec la $.each()méthode de bouclage / / etc. que vous aimez.

nnnnnn
la source
1
ne découpera pas l'index
Muhammad Umer
5
@MuhammadUmer - Non, pas si vous le faites correctement, c'est ce que ma réponse explique.
nnnnnn
5
Merci pour la prise de conscience de l'ordre inverse.
Daniel Nalbach
2
+1, je n'avais pas réalisé que je devais faire l'épissure dans l'ordre inverse, bien qu'au lieu d'utiliser forEach, mon approche utilise$.each(rvm.reverse(), function(e, i ) {})
Luis Stanley Jovel
1
cela ne fonctionnera que si removeValFromIndex est trié par ordre croissant
Kunal Burangi
24

En voici un que j'utilise lorsque je ne vais pas avec lodash / underscore:

while(IndexesToBeRemoved.length) {
    elements.splice(IndexesToBeRemoved.pop(), 1);
}
Dan Ochiana
la source
Solution élégante! Au début, je pensais que cela ne fonctionnerait pas parce que je pensais qu'à chaque fois que vous appelez, slicevous devrez recalculer les index à supprimer (-1 activé IndexestoBeRemoved), mais cela fonctionne réellement!
Renato Gama
2
Trop intelligent
Farzad YZ
11
Cette solution fonctionne si seul le IndexesToBeRemovedtableau est trié par ordre croissant.
xfg
Ces indices seront invalidés après la première épissure.
shinzou
@shinzou - Pas si IndexesToBeRemovedest trié (croissant).
nnnnnn
18

Non, in-placemais peut être fait en utilisant grepet les inArrayfonctions de jQuery.

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

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

vérifiez ce violon.

TheVillageIdiot
la source
Ce serait (assez proche de) en place si vous venez de direvaluesArr = $.grep(...);
nnnnnn
1
@nnnnnn ha ha ha Je partais pour une réunion (vous savez qu'ils vous rendent si productif) donc je n'ai pas beaucoup expérimenté.
TheVillageIdiot
1
@ cept0 Pourquoi le vote négatif? OP a demandé une solution jQuery.
Ste77
Merci beaucoup! @TheVillageIdiot
ecorvo
jsfiddler - Erreur 404. Nous sommes vraiment désolés, mais une telle page n'existe pas.
Ash
17

Je vous suggère d'utiliser 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;
})
Саша Давиденко
la source
indexOf inside of filter ... not optimum
Alvaro Joao
1
vote favorable. plus rapide que la méthode d'épissure selon jsperf.com/remove-multiple/1
lionbigcat
Génial! Maintenant je peux dormir :)
Firmansyah
7
function filtermethod(element, index, array) {  
    return removeValFromIndex.find(index)
}  
var result = valuesArr.filter(filtermethod);

La référence MDN est ici

riship89
la source
@ riship89 Le violon est à terre
mate64
@Karna: le violon est à terre
riship89
1
Veuillez noter qu'au moment de la rédaction de cet article (juin 2014), Array.prototype.find fait partie du projet ES6 actuel et n'est implémenté que dans Firefox actuel
Olli K
6

En JS pur, vous pouvez faire une boucle à travers le tableau à l'envers, donc splice()ne gâcherez pas les indices des éléments suivants dans la boucle:

for (var i = arr.length - 1; i >= 0; i--) {
    if ( yuck(arr[i]) ) {
        arr.splice(i, 1);
    }
}
Watchduck
la source
Ne fonctionne pas beurk n'est pas une fonction, donc supposé que ce soit l'index des éléments indésirables, et utilisé beurk [arr [i]]
DavChana
5

Il se sent nécessaire de poster une réponse avec le O(n)temps :). Le problème avec la solution d'épissure est qu'en raison de l'implémentation sous-jacente de array étant littéralement un tableau , chaque spliceappel prendra du O(n)temps. Ceci est plus prononcé lorsque nous configurons un exemple pour exploiter ce comportement:

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)

Cela supprime les éléments du milieu au début, donc chaque suppression oblige le moteur js à copier des n/2éléments, nous avons des (n/2)^2opérations de copie au total qui est quadratique.

La solution d'épissure (en supposant qu'elle isest déjà triée par ordre décroissant pour se débarrasser des frais généraux) se présente comme suit:

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

Cependant, il n'est pas difficile d'implémenter une solution de temps linéaire, en reconstruisant le tableau à partir de zéro, en utilisant un masque pour voir si nous copions des éléments ou non (le tri le poussera à O(n)log(n)). Ce qui suit est une telle implémentation (pas qui maskest booléen inversé pour la vitesse):

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

J'ai couru ceci sur jsperf.com et même n=100la méthode d'épissure est 90% plus lente. Pour les plus grands, ncette différence sera beaucoup plus grande.

Simonzack
la source
5

Doublure Quick ES6 one:

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
la source
Votre code doit courir plus vite si vous faites removeValFromIndexun Set()et utilisez au removeValFromIndex.haslieu de includes.
Boris
5

Une solution simple et efficace (complexité linéaire) utilisant le filtre et l' ensemble :

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

Le grand avantage de cette implémentation est que l'opération ( hasfonction) de recherche Set prend un temps constant, étant plus rapide que la réponse de nevace, par exemple.

Alberto Trindade Tavares
la source
@MichaelPaccione Heureux de vous aider :)
Alberto Trindade Tavares
3

Cela fonctionne bien pour moi et fonctionne également lors de la suppression d'un tableau d'objets:

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

Il peut y avoir une manière plus courte et plus efficace d'écrire ceci, mais cela fonctionne.

StuartMc
la source
2

Une solution simple utilisant ES5. Cela semble plus approprié pour la plupart des applications de nos jours, car beaucoup ne veulent plus s'appuyer sur jQuery, etc.

Lorsque les index à supprimer sont triés par ordre croissant:

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

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

Lorsque les index à supprimer ne sont pas triés:

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
la source
1

Vous pouvez corriger votre code en le remplaçant removeValFromIndexpar removeValFromIndex.reverse(). S'il n'est pas garanti que ce tableau utilise l'ordre croissant, vous pouvez utiliser à la place removeValFromIndex.sort(function(a, b) { return b - a }).

minopret
la source
Cela me semble bien - jsfiddle.net/mrtsherman/gDcFu/2 . Bien que cela laisse supposer que la liste de suppression est en ordre.
mrtsherman
@minopret: Merci mais cela ne fonctionnera que si les index removeValFromIndexsont dans l'ordre croissant.
xyz
1

Si vous utilisez underscore.js , vous pouvez utiliser _.filter()pour résoudre votre problème.

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

De plus, si vous essayez de supprimer des éléments en utilisant une liste d'éléments au lieu d'index, vous pouvez simplement utiliser _.without(), comme ceci:

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

Maintenant filteredArrdevrait être["V2", "V4", "V5"]

Johnny Zhao
la source
Comment contient est implémenté dans le soulignement ... faites attention si c'est équivalent à indexOf à l'intérieur du filtre ... pas optimal du tout ...
Alvaro Joao
1

filtre + indexOf (IE9 +):

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

Ou avec filtre ES6 + find (Edge +):

function removeMany(array, indexes = []) {
  return array.filter((_, idx) => indexes.indexOf(idx) === -1)
}
Daviestar
la source
indexOf inside of filter ... not optimum
Alvaro Joao
1

Voici un quickie.

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
la source
indexOf inside of filter ... not optimum
Alvaro Joao
0

On dirait que Apply pourrait être ce que vous recherchez.
peut-être que quelque chose comme ça fonctionnerait?

Array.prototype.splice.apply(valuesArray, removeValFromIndexes );
Rob
la source
Mais la .splice()méthode ne s'attend pas à une liste d'éléments à supprimer, elle attend un seul index de l'élément auquel commencer la suppression suivi du nombre d'éléments à supprimer ...
nnnnnn
0

Pour plusieurs articles ou article unique:

Je vous suggère d'utiliser Array.prototype.filter

N'utilisez jamais indexOf si vous connaissez déjà l'index!:

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

Faire:

avec Hashes ... en utilisant 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
la source
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);

Cela marche. Cependant, vous créeriez un nouveau tableau dans le processus. Je ne sais pas si c'est ce que vous voulez ou non, mais techniquement, ce serait un tableau contenant uniquement les valeurs souhaitées.


la source
-1

Vous pouvez essayer d'utiliser delete array[index]Cela ne supprimera pas complètement l'élément mais définira plutôt la valeur sur undefined.

Henesnarfel
la source
Merci mais je veux supprimer les éléments.
xyz
-1

Vous pouvez construire un à Setpartir du tableau, puis créer un tableau à partir de l'ensemble.

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]
Mohib
la source