Eliminar todos los elementos contenidos en otra matriz


222

Estoy buscando una forma eficiente de eliminar todos los elementos de una matriz de JavaScript si están presentes en otra matriz.

// If I have this array:
var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];

// and this one:
var toRemove = ['b', 'c', 'g'];

Quiero operar en myArray para dejarlo en este estado: ['a', 'd', 'e', 'f']

Con jQuery, estoy usando grep()y inArray(), que funciona bien:

myArray = $.grep(myArray, function(value) {
    return $.inArray(value, toRemove) < 0;
});

¿Existe una manera pura de javascript para hacer esto sin bucles y empalmes?




1
posible duplicado de ¿Puede eliminar una matriz de otra en javascript o jquery ? No puede haber prestado mucha atención a las sugerencias hechas cuando escribió la pregunta
mplungjan

Pase lo que pase, siempre implicará bucles en algún nivel.
Blue Skies

Si realmente quiere que sea "eficiente", no utilizará métodos de tipo funcional como .filter(). En su lugar, usarás forbucles. Puede evitar .splice()si no es necesario mantener el pedido original. O bien, hay formas de hacerlo .splice()más eficiente si cree que habrá muchos elementos para eliminar.
Blue Skies

Respuestas:


379

Usa el Array.filter()método:

myArray = myArray.filter( function( el ) {
  return toRemove.indexOf( el ) < 0;
} );

Pequeña mejora, ya que el soporte del navegador Array.includes()ha aumentado:

myArray = myArray.filter( function( el ) {
  return !toRemove.includes( el );
} );

Próxima adaptación usando las funciones de flecha :

myArray = myArray.filter( ( el ) => !toRemove.includes( el ) );

23
OP: Si está utilizando Underscore.js, hay .difference()básicamente lo que hace esto.
Bill Criswell

Justo lo que estaba buscando. Gracias. @BillCriswell, revisaré el guión bajo.
Toque

1
@AlecRust Convierta todos los elementos de toRemove()mayúsculas y cambie la devolución de llamada de ela el.toUpperCase().
Sirko

55
o menos:myArray = myArray.filter( el => !toRemove.includes( el ) );
538ROMEO

1
¿No es este orden n ^ 2?
Frazer Kirkman

34

El filtermétodo debería hacer el truco:

const myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
const toRemove = ['b', 'c', 'g'];

// ES5 syntax
const filteredArray = myArray.filter(function(x) { 
  return toRemove.indexOf(x) < 0;
});

Si su toRemovematriz es grande, este tipo de patrón de búsqueda puede ser ineficiente. Sería más eficiente crear un mapa para que las búsquedas sean O(1)más que O(n).

const toRemoveMap = toRemove.reduce(
  function(memo, item) {
    memo[item] = memo[item] || true;
    return memo;
  },
  {} // initialize an empty object
);

const filteredArray = myArray.filter(function (x) {
  return toRemoveMap[x];
});

// or, if you want to use ES6-style arrow syntax:
const toRemoveMap = toRemove.reduce((memo, item) => ({
  ...memo,
  [item]: true
}), {});

const filteredArray = myArray.filter(x => toRemoveMap[x]);

24

Si está utilizando una matriz de objetos. Luego, el siguiente código debe hacer la magia, donde una propiedad de objeto será el criterio para eliminar elementos duplicados.

En el siguiente ejemplo, se han eliminado los duplicados comparando el nombre de cada elemento.

Prueba este ejemplo. http://jsfiddle.net/deepak7641/zLj133rh/

var myArray = [
  {name: 'deepak', place: 'bangalore'}, 
  {name: 'chirag', place: 'bangalore'}, 
  {name: 'alok', place: 'berhampur'}, 
  {name: 'chandan', place: 'mumbai'}
];
var toRemove = [
  {name: 'deepak', place: 'bangalore'},
  {name: 'alok', place: 'berhampur'}
];

for( var i=myArray.length - 1; i>=0; i--){
 	for( var j=0; j<toRemove.length; j++){
 	    if(myArray[i] && (myArray[i].name === toRemove[j].name)){
    		myArray.splice(i, 1);
    	}
    }
}

alert(JSON.stringify(myArray));



11

Los conjuntos ECMAScript 6 se pueden usar para calcular los diferentes elementos de dos matrices:

const myArray = new Set(['a', 'b', 'c', 'd', 'e', 'f', 'g']);
const toRemove = new Set(['b', 'c', 'g']);

const difference = new Set([...myArray].filter((x) => !toRemove.has(x)));

console.log(Array.from(difference)); // ["a", "d", "e", "f"]


8

Acabo de implementar como:

Array.prototype.exclude = function(list){
        return this.filter(function(el){return list.indexOf(el)<0;})
}

Usar como:

myArray.exclude(toRemove);

1
No es una buena práctica extender prototypesobjetos nativos, por ejemplo Array. Eso puede tener un conflicto a largo plazo con el desarrollo futuro del lenguaje ( ver el flattencaso )
MarcoL

6

Si no puede usar cosas nuevas de ES5, filtercreo que está atrapado con dos bucles:

for( var i =myArray.length - 1; i>=0; i--){
  for( var j=0; j<toRemove.length; j++){
    if(myArray[i] === toRemove[j]){
      myArray.splice(i, 1);
    }
  }
}

filtro no es "cosas nuevas HTML5"
goofballLogic

Debería haber escrito "ES5 cosas". No estaba disponible con ES3
MarcoL

6
var myArray = [
  {name: 'deepak', place: 'bangalore'}, 
  {name: 'chirag', place: 'bangalore'}, 
  {name: 'alok', place: 'berhampur'}, 
  {name: 'chandan', place: 'mumbai'}
];
var toRemove = [
  {name: 'deepak', place: 'bangalore'},
  {name: 'alok', place: 'berhampur'}
];



myArray = myArray.filter(ar => !toRemove.find(rm => (rm.name === ar.name && ar.place === rm.place) ))

¿Le importaría agregar alguna explicación sobre una pregunta tan bien recibida, por favor?
armónica141

1
Busqué horas para la solución del problema y lo encontré, simplemente increíble. ¡Muchas gracias!
Tarvo Mäesepp

5

Ahora en sabor de una sola línea:

console.log(['a', 'b', 'c', 'd', 'e', 'f', 'g'].filter(x => !~['b', 'c', 'g'].indexOf(x)))

Puede que no funcione en navegadores antiguos.


3

Puedes usar _.differenceBy de lodash

const myArray = [
  {name: 'deepak', place: 'bangalore'}, 
  {name: 'chirag', place: 'bangalore'}, 
  {name: 'alok', place: 'berhampur'}, 
  {name: 'chandan', place: 'mumbai'}
];
const toRemove = [
  {name: 'deepak', place: 'bangalore'},
  {name: 'alok', place: 'berhampur'}
];
const sorted = _.differenceBy(myArray, toRemove, 'name');

Código de ejemplo aquí: CodePen


¿Qué pasa si el atributo está anidado dentro del objeto? Algo como en su caso {nombre: 'deepak', lugar: 'bangalore', anidado: {prueba: 1}}
Charith Jayasanka

2

¿Qué tal el más simple posible:

var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
var toRemove = ['b', 'c', 'g'];

var myArray = myArray.filter((item) => !toRemove.includes(item));
console.log(myArray)


2
Tenga en cuenta que includesno está disponible antes de ES7.
Greg

0

La forma correcta de eliminar todos los elementos contenidos en otra matriz es hacer que la matriz de origen sea el mismo objeto eliminando solo elementos:

Array.prototype.removeContained = function(array) {
  var i, results;
  i = this.length;
  results = [];
  while (i--) {
    if (array.indexOf(this[i]) !== -1) {
      results.push(this.splice(i, 1));
    }
  }
  return results;
};

O equivalente de CoffeeScript:

Array.prototype.removeContained = (array) ->
  i = @length
  @splice i, 1 while i-- when array.indexOf(@[i]) isnt -1

Prueba dentro de las herramientas de desarrollo de Chrome:

19: 33: 04.447 a = 1
19: 33: 06.354 b = 2
19: 33: 07.615 c = 3
19: 33: 09.981 arr = [a, b, c]
19: 33: 16.460 arr1 = arr

19: 33: 20.317 arr1 === arr
19: 33: 20.331 verdadero

19: 33: 43.592 arr.removeContained ([a, c])
19: 33: 52.433 arr === arr1
19: 33: 52.438 verdadero

El uso del marco angular es la mejor manera de mantener el puntero al objeto de origen cuando actualiza colecciones sin una gran cantidad de observadores y recargas.


Esta respuesta es mala ya que anula las mejores prácticas. Específicamente, nunca modifique objetos que no le pertenecen. En este caso, está modificando el objeto Array, que es un gran no-no.
Desarrollador web híbrido

0

Construyo la lógica sin usar ningún método incorporado, por favor hágame saber cualquier optimización o modificación. Probé en el editor JS que funciona bien.

var myArray = [
            {name: 'deepak', place: 'bangalore'},
            {name: 'alok', place: 'berhampur'},
            {name: 'chirag', place: 'bangalore'},
            {name: 'chandan', place: 'mumbai'},

        ];
        var toRemove = [

            {name: 'chirag', place: 'bangalore'},
            {name: 'deepak', place: 'bangalore'},
            /*{name: 'chandan', place: 'mumbai'},*/
            /*{name: 'alok', place: 'berhampur'},*/


        ];
        var tempArr = [];
        for( var i=0 ; i < myArray.length; i++){
            for( var j=0; j<toRemove.length; j++){
                var toRemoveObj = toRemove[j];
                if(myArray[i] && (myArray[i].name === toRemove[j].name)) {
                    break;
                }else if(myArray[i] && (myArray[i].name !== toRemove[j].name)){
                        var fnd = isExists(tempArr,myArray[i]);
                        if(!fnd){
                            var idx = getIdex(toRemove,myArray[i])
                            if (idx === -1){
                                tempArr.push(myArray[i]);
                            }

                        }

                    }

                }
        }
        function isExists(source,item){
            var isFound = false;
            for( var i=0 ; i < source.length; i++){
                var obj = source[i];
                if(item && obj && obj.name === item.name){
                    isFound = true;
                    break;
                }
            }
            return isFound;
        }
        function getIdex(toRemove,item){
            var idex = -1;
            for( var i=0 ; i < toRemove.length; i++){
                var rObj =toRemove[i];
                if(rObj && item && rObj.name === item.name){
                    idex=i;
                    break;
                }
            }
            return idex;
        }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.