eliminar objetos de la matriz por propiedad del objeto

138
var listToDelete = ['abc', 'efg'];

var arrayOfObjects = [{id:'abc',name:'oh'}, // delete me
                      {id:'efg',name:'em'}, // delete me
                      {id:'hij',name:'ge'}] // all that should remain

¿Cómo elimino un objeto de la matriz haciendo coincidir la propiedad del objeto?

Solo JavaScript nativo por favor.

Tengo problemas para usar el empalme porque la longitud disminuye con cada eliminación. El uso de clones y empalmes en el índice original todavía te deja con el problema de la disminución de la longitud.

Dan Kanze
fuente
1
El bucle hacia atrás debería solucionar el problema del cambio en la longitud
Ian
1
Es una pena que no haya una forma estándar adecuada para eliminar elementos de una matriz de objetos. No es de extrañar que haya tantos guiones de terceros. Es una cosa básica.
guitarlass

Respuestas:

155

¿Supongo que usaste splicealgo como esto?

for (var i = 0; i < arrayOfObjects.length; i++) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) !== -1) {
        arrayOfObjects.splice(i, 1);
    }
}

Todo lo que necesita hacer para corregir el error es disminuir ila próxima vez (y hacer un bucle hacia atrás también es una opción):

for (var i = 0; i < arrayOfObjects.length; i++) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) !== -1) {
        arrayOfObjects.splice(i, 1);
        i--;
    }
}

Para evitar eliminaciones en tiempo lineal, puede escribir elementos de matriz que desee mantener sobre la matriz:

var end = 0;

for (var i = 0; i < arrayOfObjects.length; i++) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) === -1) {
        arrayOfObjects[end++] = obj;
    }
}

arrayOfObjects.length = end;

y para evitar búsquedas de tiempo lineal en un tiempo de ejecución moderno, puede usar un conjunto de hash:

const setToDelete = new Set(listToDelete);
let end = 0;

for (let i = 0; i < arrayOfObjects.length; i++) {
    const obj = arrayOfObjects[i];

    if (setToDelete.has(obj.id)) {
        arrayOfObjects[end++] = obj;
    }
}

arrayOfObjects.length = end;

que se puede envolver en una buena función:

const filterInPlace = (array, predicate) => {
    let end = 0;

    for (let i = 0; i < array.length; i++) {
        const obj = array[i];

        if (predicate(obj)) {
            array[end++] = obj;
        }
    }

    array.length = end;
};

const toDelete = new Set(['abc', 'efg']);

const arrayOfObjects = [{id: 'abc', name: 'oh'},
                        {id: 'efg', name: 'em'},
                        {id: 'hij', name: 'ge'}];

filterInPlace(arrayOfObjects, obj => !toDelete.has(obj.id));
console.log(arrayOfObjects);

Si no necesita hacerlo en su lugar, eso es Array#filter:

const toDelete = new Set(['abc', 'efg']);
const newArray = arrayOfObjects.filter(obj => !toDelete.has(obj.id));
Ry-
fuente
79

Puede eliminar un elemento por una de sus propiedades sin usar ninguna biblioteca de terceros como esta:

var removeIndex = array.map(item => item.id)
                       .indexOf("abc");

~removeIndex && array.splice(removeIndex, 1);
parlamento
fuente
1
Esta es una respuesta realmente elegante.
Scott Silvi,
Otra cosa que quiero, habrá botones separados para cada objeto en la matriz. si quiero eliminar ese objeto en particular en el botón de matriz, haga clic y eso debería moverse a otra matriz secundaria. cómo hacerlo . He usado angular js ng-repeat para generar elementos. ¿me pueden ayudar
Thilak Raj
Enfoque muy agradable y elegante. ¡prestigio! Solo una cosa por precaución; si el índice requerido no se encuentra en la matriz del mapa, devolverá -1; así, el empalme eliminará el último elemento.
mcy
15
O si te confunde la tilde en la última línea, puedes encontrar esto más claro: (removeIndex> = 0) && array.splice (removeIndex, 1);
wojjas
1
@parliament: ¿hay alguna documentación sobre la tilde en su ejemplo? Nunca he visto esto antes.
webdad3
43

Con lodash / subrayado:

Si desea modificar la matriz existente en sí, entonces tenemos que usar el empalme . Aquí está la forma un poco mejor / legible usando findWhere de subrayado / lodash:

var items= [{id:'abc',name:'oh'}, // delete me
                  {id:'efg',name:'em'},
                  {id:'hij',name:'ge'}];

items.splice(_.indexOf(items, _.findWhere(items, { id : "abc"})), 1);

Con ES5 o superior

( sin lodash / subrayado )

Con ES5 en adelante, tenemos un findIndexmétodo en matriz, por lo que es fácil sin lodash / subrayado

items.splice(items.findIndex(function(i){
    return i.id === "abc";
}), 1);

(ES5 es compatible con casi todos los navegadores modernos)

Acerca de findIndex y su compatibilidad con el navegador

Rahul R.
fuente
¿Cómo puedo eliminar todos los artículos en serie de objetos, pero necesito la devolución de llamada, así
Muhaimin
¿Qué quiere decir con devolución de llamada también, puede utilizar el código anterior para eliminar el objeto de la matriz ..
Rahul R.
1
Buena esa. Agregaré esto a mi cinturón de herramientas
abyrne85
Otra cosa que quiero, habrá botones separados para cada objeto en la matriz. si quiero eliminar ese objeto en particular en el botón de matriz, haga clic y eso debería moverse a otra matriz secundaria. cómo hacerlo . He usado angular js ng-repeat para generar elementos. ¿me pueden ayudar
Thilak Raj
1
Me encanta esta sugerencia de ES5, ya que incluso se puede escribir de una manera más cortaitems.splice(items.findIndex(i => i.id === "abc"), 1)
bramchi
27

findIndex funciona para navegadores modernos:

var myArr = [{id:'a'},{id:'myid'},{id:'c'}];
var index = arr.findIndex(function(o){
     return o.id === 'myid';
})
if (index !== -1) myArr.splice(index, 1);
fatlinesofcode
fuente
Me gusta esta respuesta moderna, excepto que su código no
detecta
2
Respuesta muy simple en comparación con otros y funciona bien.
Arun
Respuestas simples pero poderosas
Shelly
15

Para eliminar un objeto por su id en una matriz dada;

const hero = [{'id' : 1, 'name' : 'hero1'}, {'id': 2, 'name' : 'hero2'}];
//remove hero1
const updatedHero = hero.filter(item => item.id !== 1);
Naresh Chennuri
fuente
10

Si solo desea eliminarlo de la matriz existente y no crear uno nuevo, intente:

var items = [{Id: 1},{Id: 2},{Id: 3}];
items.splice(_.indexOf(items, _.find(items, function (item) { return item.Id === 2; })), 1);
user2704940
fuente
77
tenga en
2
No recomendaría esto porque si no se encuentra el elemento _.indexOf devolverá -1 => items.splice (-1,1)
user1441287
6

Haga un bucle inverso al disminuir ipara evitar el problema:

for (var i = arrayOfObjects.length - 1; i >= 0; i--) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) !== -1) {
        arrayOfObjects.splice(i, 1);
    }
}

O use filter:

var newArray = arrayOfObjects.filter(function(obj) {
    return listToDelete.indexOf(obj.id) === -1;
});
Felix Rabe
fuente
5

Mira esto usando Set y el filtro ES6.

  let result = arrayOfObjects.filter( el => (-1 == listToDelete.indexOf(el.id)) );
  console.log(result);

Aquí está JsFiddle: https://jsfiddle.net/jsq0a0p1/1/

Miroslav Savovski
fuente
1
¡Funciona perfectamente!
Antoni Kepinski
Enfoque más moderno y elegante en mi opinión.
AC Patrice
4

Solo JavaScript nativo por favor.

Como alternativa, una solución más "funcional", trabajando en ECMAScript 5, puede usar:

var listToDelete = ['abc', 'efg'];
var arrayOfObjects = [{id:'abc',name:'oh'}, // delete me
                      {id:'efg',name:'em'}, // delete me
                      {id:'hij',name:'ge'}]; // all that should remain

arrayOfObjects.reduceRight(function(acc, obj, idx) {
    if (listToDelete.indexOf(obj.id) > -1)
        arrayOfObjects.splice(idx,1);
}, 0); // initial value set to avoid issues with the first item and
       // when the array is empty.

console.log(arrayOfObjects);
[ { id: 'hij', name: 'ge' } ]

De acuerdo con la definición de 'Array.prototype.reduceRight' en ECMA-262 :

reduceRight no muta directamente el objeto en el que se llama, pero el objeto puede ser mutado por las llamadas a callbackfn .

Entonces este es un uso válido de reduceRight.

Sylvain Leroux
fuente
2
var arrayOfObjects = [{id:'abc',name:'oh'}, // delete me
                      {id:'efg',name:'em'}, // delete me
                      {id:'hij',name:'ge'}] // all that should remain

según su respuesta será así. Cuando hace clic en algún objeto en particular, envíe el índice en el parámetro para la función Eliminarme. Este código simple funcionará como encanto.

function deleteme(i){
    if (i > -1) {
      arrayOfObjects.splice(i, 1);
    }
}
Subhojit Mondal
fuente
1
Gracias esto ayudó mucho :)
RAJESH KUMAR ARUMUGAM
1

con filtro e index

withLodash = _.filter(arrayOfObjects, (obj) => (listToDelete.indexOf(obj.id) === -1));
withoutLodash = arrayOfObjects.filter(obj => listToDelete.indexOf(obj.id) === -1);

con filtro e incluye

withLodash = _.filter(arrayOfObjects, (obj) => (!listToDelete.includes(obj.id)))
withoutLodash = arrayOfObjects.filter(obj => !listToDelete.includes(obj.id));
usuario3437231
fuente
0

Si le gustan los parámetros breves y autodescriptivos o si no desea usar splicee ir con un filtro directo o si simplemente es una persona SQL como yo:

function removeFromArrayOfHash(p_array_of_hash, p_key, p_value_to_remove){
    return p_array_of_hash.filter((l_cur_row) => {return l_cur_row[p_key] != p_value_to_remove});
}

Y una muestra de uso:

l_test_arr = 
[
    {
         post_id: 1,
        post_content: "Hey I am the first hash with id 1"
    },
    {
        post_id: 2,
        post_content: "This is item 2"
    },
    {
        post_id: 1,
        post_content: "And I am the second hash with id 1"
    },
    {
        post_id: 3,
        post_content: "This is item 3"
    },
 ];



 l_test_arr = removeFromArrayOfHash(l_test_arr, "post_id", 2); // gives both of the post_id 1 hashes and the post_id 3
 l_test_arr = removeFromArrayOfHash(l_test_arr, "post_id", 1); // gives only post_id 3 (since 1 was removed in previous line)
Mehmet Kaplan
fuente
-1

Puedes usar filter. Este método siempre devuelve el elemento si la condición es verdadera. Entonces, si desea eliminar por identificación, debe mantener todo el elemento que no coincida con la identificación dada. Aquí hay un ejemplo:

arrayOfObjects = arrayOfObjects.filter (obj => obj.id! = idToRemove)

David Benitez Riba
fuente