Eliminar un elemento de un array en Javascript (métodos mutables e inmutables)

09 Sep 2016

Introducción

Con la reciente adopción del nuevo estándar ECMAScript2015, ‘encontramos una no mejora’ que siempre es conveniente resaltar: continuamos sin tener un método nativo para eliminar valores de un array dado.

Como esta funcionalidad es recurrente y necesaria, vamos a implementar una simple función en Javascript plano que nos resuelva el problema…

El problema

Tenemos este array:

var foo = [ 'thumb-1', 'thumb-2', 'thumb-3', 'thumb-4' ];

Y queremos borrar un elemento; digamos por ejemplo ‘thumb-2‘.

Opción mala

Ya que no tenemos un método ‘remove‘ en el objeto global Array, necesitamos utilizar alguno de los disponibles. El más recurrente sería en este caso ‘splice’, el cual cambia el contenido de un array eliminando o agregando elementos:

function removeItemFromArr ( arr, item ) {
    var i = arr.indexOf( item );
    arr.splice( i, 1 );
}
 
removeItemFromArr( foo, 'thumb-2' );
console.info( foo );
// ["thumb-1", "thumb-3", "thumb-4"]

El problema con esta función es que si tratamos de borrar un elemento que no existe, el resultado no será el deseado:

removeItemFromArr( foo, 'picture-1' );
console.info( foo );
// ["thumb-1", "thumb-2", "thumb-3"]

Obsérvese que el último elemento, ‘thumb-4‘, ha desaparecido. Esto es así porque el valor que toma ‘i‘ tras no encontrar ‘indexOf‘ una concurrencia, es -1. Por lo tanto, al método splice le estamos indicando que borre ‘-1’ o, lo que es lo mismo, el último elemento del array (o el primero empezando por el final).

Opción más segura

Para evitar el problema con elementos que no existen, necesitamos comprobar el valor de ‘i‘ antes de ejecutar el splice:

function removeItemFromArr ( arr, item ) {
    var i = arr.indexOf( item );
 
    if ( i !== -1 ) {
        arr.splice( i, 1 );
    }
}
 
removeItemFromArr( foo, 'picture-1' );
console.info( foo );
// ["thumb-1", "thumb-2", "thumb-3", "thumb-4"]
 
removeItemFromArr( foo, 'thumb-2' );
console.info( foo );
// ["thumb-1", "thumb-3", "thumb-4"]

No está mal; es una opción válida. Podemos formatear un poco la función para que tenga mejor aspecto sin alterar su comportamiento:

var removeItemFromArr = ( arr, item ) => {
    var i = arr.indexOf( item );
    i !== -1 && arr.splice( i, 1 );
};

Opción no mutable

Otro problema que podemos encontrar ahora es el de la no inmutabilidad: la función anterior toma un array y lo modifica, haciendo imposible volver a su estado original. En ciertos escenarios y paradigmas, como bajo la Programación Funcional, el concepto de mutabilidad no está permitido.

Propongamos una primera versión inmutable de nuestra función utilizando para ello el método Array.filter():

function removeItemFromArr( arr, item ) {
    return arr.filter( function( e ) {
        return e !== item;
    } );
};
 
var newFoo = removeItemFromArr( foo, 'thumb-2' );
 
console.info( foo );
// ["thumb-1", "thumb-2", "thumb-3", "thumb-4"]
 
console.info( newFoo );
// ["thumb-1", "thumb-3", "thumb-4"]

La cosa funciona correctamente y, de nuevo, es tolerante frente a elementos que no se encuentren en el array original:

var newFoo = removeItemFromArr( foo, 'picture-1' );
 
console.info( newFoo );
// ["thumb-1", "thumb-2", "thumb-3", "thumb-4"]

Reformateamos para que quede más limpia:

var removeItemFromArr = ( arr, item ) => {
    return arr.filter( e => e !== item );
};

Conclusión

Aquí hemos presentado unos cuando métodos para borrar elementos de un array intentando así suplir una carencia histórica del lenguaje Javascript. Es cierto que con los nuevas estructuras de datos de tipo Set (ECMA-262), este problema se resuelve parcialmente al contar ya con un método ‘delete’ nativo; no obstante, su comportamiento y semátinca difiere en parte de los arrays tradicionales.

Más:

{2} Comentarios.

  1. Miguel

    He entrado buscando algún comentario sobre rendimiento, quizás implementaciones alternativas y las diferencias en rendimiento.

    Splice siempre va a ser lento como el demonio. Si tu código require muchas inserciones, definitivamente Array no es la estructura de datos correcta. Una lista enlazada siempre irá mejor.

    • Carlos Benítez

      Sí; quizá puedo añadir un apartado sobre rendimiento en diferentes métodos… tienes razón cuando trabajamos con arrays grandes.
      Gracias por el apunte!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *