🖖 Inicio

SERVICIOS

Salir

nochedía

Cómo copiar un array en JavaScript?

600 palabras
2 minutos
June 21, 2019
blogjavascript

Te explico cómo copiar arrays en JavaScript y por qué no es tan sencillo como parece

  1. Prólogo
  2. Copia simple
  3. Copia shallow con Array.from
  4. Copia shallow con Spread Operator
  5. Copia deep
  6. Resumen

Prólogo

Al igual que vimos con objects en cómo copiar un object, clonar arrays no es tarea fácil ya que en JavaScript todo se copia por referencia

La frase `todo se copia por referencia` es en realidad incorrecta, en `JavaScript` todo se copia por `valor`, pero los valores de todo lo que no son `primitivas` contienen referencias con lo que es "como" si se pasasen referencias

Si quieres constatar lo enrevesado que es este tema, tienes esta pregunta de stack overflow para entretenerte

Copiar un array y no simplemente modificarlo no es tan sencillo como puede parecer, ya que un array en el fondo es un object

Y por lo tanto y al igual que con objetos, cuando creemos copiar un array lo que estaremos haciendo en realidad será copiar una referencia, con lo que en lugar de tener 2 copias tendremos 2 variables que apuntarán a un único array

Cómo hacemos entonces una copia real y que podamos modificar independientemente?

Copia simple

Siguiendo con la misma estructura del artículo de los object, con el siguiente código vemos que la copia que pretendo no es tal

js
let myArr = ['https://www.kuworking.com/']
console.log(myArr[0]) // https://www.kuworking.com/
let myArrCopy = myArr
console.log(myArrCopy[0]) // https://www.kuworking.com/
myArr[0] = 'https://www.kuworking2.com'
console.log(myArr[0]) // https://www.kuworking2.com
console.log(myArrCopy[0]) // https://www.kuworking2.com

Esto ocurre porque tanto myArr como myArrCopy son variables que apuntan a la misma referencia del array

Por lo tanto no son copias del array sino de la dirección del array

Siguiendo el símil del edificio, con esta manera de copiar edificios lo que en realidad he hecho es copiar la dirección del edificio, no el edificio en sí

Copia shallow con Array.from

Como con los objetos y su Object.assign(), una copia shallow de arrays se puede hacer con un Array.from(), y lo podemos ver fácilmente con este código

js
let myArr = [
'https://www.kuworking.com/',
{
data: {
type: 'blog',
location: 'barcelona',
},
},
[
'copiando un array multidimensional',
],
]
let myArrCopy = Array.from(myArr)
myArrCopy[0] = 'https://www.kuworking2.com'
myArrCopy[1].data.type = 'laptops'
myArrCopy[2][0] = 'ya está copiado'
console.log(myArr)
console.log(myArrCopy)
/*
[
"https://www.kuworking.com/",
{
"data": {
"type": "laptops",
"location": "barcelona"
}
},
[
"ya está copiado"
]
]
[
"https://www.kuworking2.com",
{
"data": {
"type": "laptops",
"location": "barcelona"
}
},
[
"ya está copiado"
]
]
*/

Este array es multidimensional y tiene en array[0] una string, en array[1] un object y en array[2] un array que asimismo tiene un string en array[2][0]

Recuerda que tanto el objeto como el array son referencias y por lo tanto, una copia que no sea deep se limitará a copiar esas referencias

Y es que esto es lo que precisamente vemos con Array.from() (lo mismo que veíamos con Object.assign()), es decir, que cuando cambio el contenido de esas referencias estoy cambiando el valor de las 2 copias, y por lo tanto no eran en realidad copias completas

No hay que olvidar que en JavaScript casi todo es un objeto, arrays incluidos

Copia shallow con Spread Operator

Si repetimos lo mismo pero con el spread operator tenemos lo siguiente

js
let myArr = [
'https://www.kuworking.com/',
{
data: {
type: 'blog',
location: 'barcelona',
},
},
[
'copiando un array multidimensional',
],
]
let myArrCopy = [...myArr]
myArrCopy[0] = 'https://www.kuworking2.com'
myArrCopy[1].data.type = 'laptops'
myArrCopy[2][0] = 'ya está copiado'
console.log(myArr)
console.log(myArrCopy)
/*
[
"https://www.kuworking.com/",
{
"data": {
"type": "laptops",
"location": "barcelona"
}
},
[
"ya está copiado"
]
]
[
"https://www.kuworking2.com",
{
"data": {
"type": "laptops",
"location": "barcelona"
}
},
[
"ya está copiado"
]
]
*/

Y vemos que es exactamente el mismo caso que con Array.from(), es decir, el contenido ha cambiado en las 2 copias

Copia deep

Para obtener una copia completa, es decir que pueda cambiar una de las 2 copias sin afectar a la otra copia (deep clone), al final terminamos igual que con los objects, esto es, utilizando la fórmula JSON

De este modo lo que hacemos es forzar JavaScript a que cree nuevas estructuras de datos, generando por lo tanto copias completas

El uso de JSON-stringify y JSON.parse no es la solución a todo, como explican en Dev.to, y es que si tenemos funciones o estructuras no compatibles con json las perderemos en la copia

Si realmente tenemos estructuras no compatibles con json y necesitamos hacer deep copies de esos arrays, del enlace anterior de Dev.to sacamos esta solución recursiva que si bien resulta algo ofuscada es eficiente y compacta

js
const clone = items => items.map(item => (Array.isArray(item) ? clone(item) : item))

Esta función simplemente itera sobre todos los elementos del array y se interroga con Array.isArray() si es que item es un array o no

El código por lo tanto irá iterando sobre todos los arrays que encuentre creando copias completas pero sólo si son arrays, por lo que si tenemos elementos que no lo sean, estos elementos (objects, funciones, etc) se perderan (se copiarán como primitivas, es decir como si fueran strings)

Lo puedes ver en este código

js
let myArr = [
'https://www.kuworking.com/',
{
data: {
type: 'blog',
location: 'barcelona',
},
},
[
'copiando un array multidimensional',
],
]
let myArrCopy = JSON.parse(JSON.stringify(myArr))
const clone = (items) => items.map(item => Array.isArray(item) ? clone(item) : item);
let myArrCopy2 = clone(myArr)
myArrCopy[0] = 'https://www.kuworking2.com'
myArrCopy[1].data.type = 'laptops'
myArrCopy[2][0] = 'ya está copiado'
myArrCopy2[0] = 'https://twitter.com/kuworking'
myArrCopy2[1].data.type = 'twitter'
myArrCopy2[2][0] = 'y aquí también está copiado'
console.log(myArr)
console.log(myArrCopy)
console.log(myArrCopy2)
/*
[
"https://www.kuworking.com/",
{
"data": {
"type": "twitter",
"location": "barcelona"
}
},
[
"copiando un array multidimensional"
]
]
[
"https://www.kuworking2.com",
{
"data": {
"type": "laptops",
"location": "barcelona"
}
},
[
"ya está copiado"
]
]
[
"https://twitter.com/kuworking",
{
"data": {
"type": "twitter",
"location": "barcelona"
}
},
[
"y aquí también está copiado"
]
]
*/

Esto es

  1. Con JSON puedo hacer un deep clone aunque con las limitaciones antes comentadas
  2. Mientras que con nuestro método recursivo clone podemos hacer un deep clone de todo lo que sean arrays, pero no funciona si tenemos otros tipos de objetos como los objects (como es el caso)

Resumen

Copiar objetos y arrays debería ser algo simple y sencillo, y si tenemos una imperiosa necesidad podemos acudir a librerías muy populares como lodash que implementan las funciones pertinentes

Pero lo que seguro que es importante es que entiendas por qué no es una tarea trivial y por qué las soluciones propuestas funcionan (al menos en parte)

Nada más sencillo que esto 😀

Qué tal la entrada?
👌 Bien 🙌🙌
👍 Bien, pero algunas cosas podrían explicarse mejor 😬
🤷‍♂️ Da por sentadas demasiadas cosas 😒
🤷‍♂️ A ver, hay poca chicha 😬
🤷‍♂️ Los ejemplos no son muy claros 🙇‍♂️
🤷‍♂️ No se entiende, está mal escrito 👎
✍️ Hay errores, revísalo en cuanto puedas 🙏

Newsletter de kuworking, un correo de tanto en cuanto

Quizá te interese

Privacidad
by kuworking.com
[ 2020 >> kuworking ]