IronWoods.es

Desarrollo web

Blog / JavaScript / Clonar arrays en JavaScript

Para duplicar un array en JavaScript hay que tener en cuenta los conceptos de mutable e inmutable.

Asignar un array a otra variable

Asignar el array de una variable a una nueva variable crea una referencia en memoria. La nueva variable no es un array diferente. Modificar el array de la variable original o la nueva, modifica indistintamente el contenido de ambas.


    const fruits = [
    'orange',
    'lemon'
    ];

    // Asignación:
    const newFruits = fruits;

    // Pruebas:
    console.log(fruits === newFruits);
    // true

    newFruits[1] = 'banana';
    console.log(fruits, newFruits);
    // orange, banana
    // orange, banana

Clonar un array

Hay procedimientos para clonar un array "superficialmente", esto es a un nivel, lo que funciona con tipos primitivos, que son inmutables: string, number, boolean, null, undefined y symbol.


    const fruits = [
    'orange',
    'lemon'
    ];

    // Clonado:
    const clonedFruits    = fruits.slice();
    const clonedFruitsES6 = [...fruits]; // usa "sprite"

    // Pruebas:
    console.log(fruits === clonedFruits);
    console.log(fruits === clonedFruitsES6);
    // false
    // false

    clonedFruits[1]    = 'banana';
    clonedFruitsES6[1] = 'apple';

    console.log(fruits, clonedFruits, clonedFruitsES6);
    // orange, lemon
    // orange, banana
    // orange, apple

Clonar un array "profundamente"

Si el array no contiene primitivas los métodos anteriores no funcionan correctamente, es decir, un array con objetos, arrays o funciones en su interior requiere de un procedimiento de clonado diferente para evitar errores en la aplicación.

Imaginemos que el array de frutas contiene objetos Fruta, cada una tiene un nombre, peso, precio, ... Si repitiéramos el clonado superficial como en el ejemplo anterior y las pruebas:


    console.log(fruits === clonedFruits);
    console.log(fruits === clonedFruitsES6);

Volveríamos a tener el mismo resultado. Los arrays son diferentes, o apuntan a direcciones de memoria diferentes, pero NO su contenido... ¡CUIDADO!

Veamos un ejemplo completo:


    class Fruit
    {
        constructor(name, prize)
        {
            this.name  = name;
            this.prize = prize;
        }
    }

    const fruit1 = new Fruit(
        'orange',
        1.25
    );
    const fruit2 = new Fruit(
        'lemon',
        1.60
    );

    const fruits = [fruit1, fruit2];

    // Clonado:
    const clonedFruits    = fruits.slice();
    const clonedFruitsES6 = [...fruits]; // usa "sprite"

    // Pruebas:
    console.log(fruits === clonedFruits);
    console.log(fruits === clonedFruitsES6);
    // false
    // false

    // En este punto todo parece funcionar "normalmente"
    // Ahora voy a cambiarle el nombre a una de las
    // frutas a cada uno de los nuevos arrays:

    clonedFruits[1].name    = 'banana';
    clonedFruitsES6[1].name = 'apple';

    // Si el clonado funcionará como se espera,
    // la siguiente instrucción:
    console.log(fruits, clonedFruits, clonedFruitsES6);

    // daría un resultado como este:
    // 0: Fruit { name:orange, prize: 1.25}
    // 0: Fruit { name:lemon, prize: 1.60}
    // 0: Fruit { name:orange, prize: 1.25}
    // 0: Fruit { name:banana, prize: 1.60}
    // 0: Fruit { name:orange, prize: 1.25}
    // 0: Fruit { name:apple, prize: 1.60}

    // Obtenemos este otro:
    // 0: Fruit { name:orange, prize: 1.25}
    // 0: Fruit { name:apple, prize: 1.60}
    // 0: Fruit { name:orange, prize: 1.25}
    // 0: Fruit { name:apple, prize: 1.60}
    // 0: Fruit { name:orange, prize: 1.25}
    // 0: Fruit { name:apple, prize: 1.60}

Ahora, el ejemplo con el clonado profundo, para obtener lo esperado: tres arrays distintos en forma y contenido. Sólo cambian las dos líneas donde se hace el clonado:


    // Clonado profundo:
    const clonedFruits
        = JSON.parse(JSON.stringify(fruits));
    const clonedFruitsES6
        = JSON.parse(JSON.stringify(fruits));

NOTA: he mantenido los nombres de los arrays (clonedFruits y clonedFruitsES6) por coherencia con los ejemplos completos, si bien no tiene sentido aquí diferencia la "vieja vía" de la ES6, igual podían haber sido newFruits1 y newFruits2.