hand Inicio
hand JSBloqs
hand GutenBloqs
Qu茅?
noched铆a

DESARROLLO WEB con
REACT y WORDPRESS

Ap煤ntate a la newsletter (escribo algo de tanto en cuanto)

C贸mo copiar un array en JavaScript?

900 palabras
4 minutos
October 9, 2020
blogjavascript

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

  1. Todo por referencia (o era valor?)
  2. Cu谩l es el problema con clonar arrays?
  3. Copiando el array, copia simple
  4. Copia shallow
  5. Copia deep
  6. Copia deep casi completa con JSON

Todo por referencia (o era valor?)

Al igual que vimos con objects en c贸mo copiar un object, clonar arrays no es trivial ya que en JavaScript todo se copia por referencia

Qu茅 es copiar por referencia?

En JavaScript cuando se copia un valor que NO es primitivo, en lugar de copiar el contenido de ese valor se copia una referencia a ese contenido

El s铆mil con edificios ser铆a que copiar por valor ser铆a construir un edificio igual al que queremos copiar, y por lo tanto terminar铆amos con 2 edificios iguales, mientras que copiar por referencia ser铆a simplemente copiar la direcci贸n, por lo que tendr铆amos 2 papeles con la direcci贸n pero un 煤nico edificio

Pero qu茅 es un valor primitivo?

A efectos pr谩cticos, es un string, un number o un boolean

Es decir

  • cuando copiamos un string, un number o un boolean estamos copiando el valor
  • pero cuando copiamos por ejemplo un array estamos copiando la direcci贸n de ese array, la referencia

Dicho esto, cuando se quiere clonar una variable lo que se quiere es hacer una copia por valor y no por referencia

Y no es trivial

*si quieres entretenerte, aqu铆 tienes la pregunta del mill贸n en stack overflow

Una manera pr谩ctica de verlo es con el uso de const

const y let son los sustitutos de var en ES6 (la versi贸n "moderna" de JavaScript)

Cualquier variable que definimos con const ser谩 una constante, no podr谩 cambiarse nunca jam谩s

Por ejemplo, esto de aqu铆 no podemos hacerlo

js
const mistring = 'hola'
mistring = 'hola otra vez' // ERROR, la variable mistring ya ha sido definida y es una constante!!

Esto falla porque estamos asignando un nuevo valor a la misma variable que ya se ha definido antes como constante

Con un array tenemos lo mismo

js
const miarray = ['hola']
miarray = ['hola otra vez'] // ERROR, la variable miarray ya ha sido definida y es una constante!!

Estamos asignando un array a una variable que ya ha sido definida, y no nos deja

Sin embargo, esto s铆 que podemos hacerlo

js
const miarray = []
miarray[0] = 'hola'
miarray[0] = 'hola otra vez' // ning煤n error
console.log(miarray) // ["hola otra vez"]

Y esto podemos hacerlo porque cuando utilizamos miarray[0] estamos accediendo a la primera posici贸n del array, y esa posici贸n no es una constante

Y podemos acceder de este modo porque miarray almacena una referencia y no un valor

Entender esto es importante para ver por qu茅 copiar un array no es sencillo, y es porque una cosa es copiar valores y otra cosa es copiar referencias

Cu谩l es el problema con clonar arrays?

Con lo dicho arriba, puedes ver que el problema en copiar un array es que est谩s copiando una referencia y no un valor

Y cuando copias una referencia, pasan cosas como 茅sta:

js
const miarray = ['Me caes muy bien', 'Filomena']
const miarray2 = miarray
miarray2[0] = 'Me caes fatal'
miarray2[1] = 'Eustakio'
console.log(miarray) // ["Me caes fatal", "Eustakio"]
console.log(miarray2) // ["Me caes fatal", "Eustakio"]
Ver en CodeSandBox

Lo suyo hubiera sido tener dos copias del array con contenido distinto e independiente, pero vemos que cuando cambiamos el segundo array tambi茅n estamos cambiando el primero, y no es lo que queremos

O en otras palabras, en este ejemplo mi intento de clonar un array ha fallado estrepitosamente

Copiando el array, copia simple

La copia simple simplemente copia la referencia del array, es lo que hemos visto arriba y no nos sirve si lo que queremos es clonar ese array

js
const myArr = ['馃槑馃槑馃槑馃槑']
const myCopy = myArr
myArr[0] = '馃敟馃敟馃敟馃敟'
console.log(myArr) // ["馃敟馃敟馃敟馃敟"]
console.log(myCopy) // ["馃敟馃敟馃敟馃敟"]
Ver en CodeSandBox

Copia shallow

Una copia shallow de arrays se refiere a copiar la primera "capa" o la primera dimensi贸n del array

Se puede hacer con un Array.from() o con el spread operator, que lo que hace es "desplegarnos" la estructura de datos

js
const myCopy = myArr // copia simple
const myShallowCopy = Array.from(myArr) // copia shallow, con el Array.from
const myShallowCopy2 = [...myArr] // copia shallow, con el spread operator

Y para ver si estos m茅todos funcionan o no, hago lo mismo que antes, cambio los valores del array original y miro si los de la copia han cambiado o se mantienen independientes

js
const myArr = [
'馃槑馃槑馃槑馃槑', // una dimensi贸n
'馃槑馃槑馃槑馃槑', // una dimensi贸n
'馃槑馃槑馃槑馃槑', // una dimensi贸n
['馃馃馃馃', '馃寷馃寷馃寷馃寷'], // dos dimensiones
{ myOtherArray: ['馃馃従馃馃従馃馃従馃馃従'] }, // tres dimensiones
]
const myCopy = myArr // copia simple
const myShallowCopy = Array.from(myArr) // copia shallow, con el Array.from
const myShallowCopy2 = [...myArr] // copia shallow, con el spread operator
// ahora cambio las variables de mi array inicial y as铆 vemos c贸mo de independiente es mi copia
myArr[0] = '馃敟馃敟馃敟馃敟'
myArr[1] = '馃敟馃敟馃敟馃敟'
myArr[2] = '馃敟馃敟馃敟馃敟'
myArr[3][0] = '馃敟馃敟馃敟馃敟'
myArr[3][1] = '馃敟馃敟馃敟馃敟'
myArr[4].myOtherArray[0] = '馃敟馃敟馃敟馃敟'
// el array original y la copia simple, son id茅nticas
console.log(myArr) // ["馃敟馃敟馃敟馃敟", "馃敟馃敟馃敟馃敟", "馃敟馃敟馃敟馃敟", ["馃敟馃敟馃敟馃敟", "馃敟馃敟馃敟馃敟"], {myOtherArray: ["馃敟馃敟馃敟馃敟"]}]
console.log(myCopy) // ["馃敟馃敟馃敟馃敟", "馃敟馃敟馃敟馃敟", "馃敟馃敟馃敟馃敟", ["馃敟馃敟馃敟馃敟", "馃敟馃敟馃敟馃敟"], {myOtherArray: ["馃敟馃敟馃敟馃敟"]}]
// Las copias shallow, ves que ha protegido la primera dimensi贸n del array, pero no las dem谩s
console.log(myArr) // ["馃敟馃敟馃敟馃敟", "馃敟馃敟馃敟馃敟", "馃敟馃敟馃敟馃敟", ["馃敟馃敟馃敟馃敟", "馃敟馃敟馃敟馃敟"], {myOtherArray: ["馃敟馃敟馃敟馃敟"]}]
console.log(myShallowCopy) // ["馃槑馃槑馃槑馃槑", "馃槑馃槑馃槑馃槑", "馃槑馃槑馃槑馃槑", ["馃敟馃敟馃敟馃敟", "馃敟馃敟馃敟馃敟"], {myOtherArray: ["馃敟馃敟馃敟馃敟"]}]
console.log(myShallowCopy2) // ["馃槑馃槑馃槑馃槑", "馃槑馃槑馃槑馃槑", "馃槑馃槑馃槑馃槑", ["馃敟馃敟馃敟馃敟", "馃敟馃敟馃敟馃敟"], {myOtherArray: ["馃敟馃敟馃敟馃敟"]}]
Ver en CodeSandBox

Es decir, myArr y myShallowCopy o myShallowCopy2 son independientes en la primera dimensi贸n, pero en las dem谩s son el mismo objeto, con lo que para esas dimensiones si cambio el original tambi茅n cambio la copia

Copia deep

Si una copia shallow era "superficial", una copia deep busca hacer una copia completa de todo el array, una copia de todas sus dimensiones

Y para hacerlo tenemos distintas maneras:

  • De forma manual con un loop que nos itere por todos los lados

  • Utilizando alguna librer铆a ultra-famosa como lodash

Vamos a ver los dos m茅todos, el primero (el manual) utilizando la soluci贸n de samanthaming, y el segundo con la librer铆a lodash

(si vas al codesandbox tendr谩s que a帽adir la dependencia de lodash)

js
// importo lodash
import _ from 'lodash'
// funci贸n manual para clonar arrays
const clone = items => items.map(item => (Array.isArray(item) ? clone(item) : item))
// el array original
const myArr = ['馃槑馃槑馃槑馃槑', ['馃槑馃槑馃槑馃槑'], { myOtherArray: ['馃槑馃槑馃槑馃槑'] }]
// copia shallow
const myShallowCopy = [...myArr]
// copia deep manual
const myDeepCopy = clone(myArr)
// copia deep con los dos m茅todos de lodash, uno para shallow y otro para deep
const myShallowLodashClone = _.clone(myArr)
const myDeepLodashClone = _.cloneDeep(myArr)
// cambio el array original
myArr[0] = '馃敟馃敟馃敟馃敟' // 1 dimensi贸n
myArr[1][0] = '馃敟馃敟馃敟馃敟' // 2 dimensiones
myArr[2].myOtherArray[0] = '馃敟馃敟馃敟馃敟' // 3 dimensiones
// Y a ver qu茅 nos dan los clones
console.log(myArr) // ["馃敟馃敟馃敟馃敟", ["馃敟馃敟馃敟馃敟"], {myOtherArray: ["馃敟馃敟馃敟馃敟"]}]
console.log(myShallowCopy) // ["馃槑馃槑馃槑馃槑", ["馃敟馃敟馃敟馃敟"], {myOtherArray: ["馃敟馃敟馃敟馃敟"]}]
console.log(myDeepCopy) // ["馃槑馃槑馃槑馃槑", ["馃槑馃槑馃槑馃槑"], {myOtherArray: ["馃敟馃敟馃敟馃敟"]}]
console.log(myShallowLodashClone) // ["馃槑馃槑馃槑馃槑", ["馃敟馃敟馃敟馃敟"], {myOtherArray: ["馃敟馃敟馃敟馃敟"]}]
console.log(myDeepLodashClone) // ["馃槑馃槑馃槑馃槑", ["馃槑馃槑馃槑馃槑"], {myOtherArray: ["馃槑馃槑馃槑馃槑"]}]
Ver en CodeSandBox
  • Las copias shallow s贸lo copian la primera dimensi贸n, como hemos visto antes
  • La copia deep con la funci贸n propia no consigue copiar todas las dimensiones
  • Y una copia completa s铆 la conseguimos con _.cloneDeep de lodash

Podr铆amos modificar la funci贸n para conseguir los mismos resultados de lodash, de hecho la tienes en lodash.clonedeep, por lo que no tienes que importar toda la librer铆a

O directamente puedes ir al github, ver la funci贸n, y utilizarla tu mismo

Copia deep casi completa con JSON

Hay otro m茅todo tremendamente pr谩ctico y que no requiere de librer铆as externas

Es el m茅todo JSON, y se trata de convertir nuestro array en un objeto JSON, y luego volver a convertir ese objeto en un nuevo array

En el proceso las referencias se consolidan en nuevos objetos, con lo que aunque la funci贸n JSON no est谩 pensada para clonar datos, nos sirve perfectamente a este prop贸sito

js
// el array original
const myArr = ['馃槑馃槑馃槑馃槑', ['馃惀馃惀馃惀馃惀'], { myOtherArray: ['馃馃馃馃'] }]
// la copia con JSON
const myJSONCopy = JSON.parse(JSON.stringify(myArr))
// cambio el array original
myArr[0] = '馃敟馃敟馃敟馃敟' // 1 dimensi贸n
myArr[1][0] = '馃敟馃敟馃敟馃敟' // 2 dimensiones
myArr[2].myOtherArray[0] = '馃敟馃敟馃敟馃敟' // 3 dimensiones
// Y a ver qu茅 nos da
console.log(myArr) // ["馃敟馃敟馃敟馃敟", ["馃敟馃敟馃敟馃敟"], {myOtherArray: ["馃敟馃敟馃敟馃敟"]}]
console.log(myJSONCopy) // ["馃槑馃槑馃槑馃槑", ["馃惀馃惀馃惀馃惀"], {myOtherArray: ["馃馃馃馃"]}]
Ver en CodeSandBox

Conseguimos clonar todo el objeto como hac铆amos antes con el _.cloneDeep de lodash

  • Ventajas? Se lee muy f谩cilmente y no dependes de una librer铆a externa
  • Desventajas? Cierta incompatibilidad con algunos tipos de datos que no se pueden clonar bien (como Date)

En cuanto a velocidad, haciendo un benchmark con measurethat.net vemos lo siguiente:

  • Con estructuras sencillas, la soluci贸n con lodash es un 1.5x m谩s r谩pida que la soluci贸n con JSON
  • Con estructuras "algo" complejas como la que ver谩s debajo, la soluci贸n JSON ya empieza a ir m谩s r谩pido que la de lodash
js
const myArr = [
'馃槑馃槑馃槑馃槑',
['馃惀馃惀馃惀馃惀'],
{ myOtherArray: ['馃馃馃馃'] },
'馃槑馃槑馃槑馃槑',
['馃惀馃惀馃惀馃惀'],
{ myOtherArray: ['馃馃馃馃'] },
'馃槑馃槑馃槑馃槑',
['馃惀馃惀馃惀馃惀'],
{ myOtherArray: ['馃馃馃馃'] },
'馃槑馃槑馃槑馃槑',
['馃惀馃惀馃惀馃惀'],
{ myOtherArray: ['馃馃馃馃'] },
'馃槑馃槑馃槑馃槑',
['馃惀馃惀馃惀馃惀'],
{ myOtherArray: ['馃馃馃馃'] },
'馃槑馃槑馃槑馃槑',
['馃惀馃惀馃惀馃惀'],
{ myOtherArray: ['馃馃馃馃'] },
'馃槑馃槑馃槑馃槑',
['馃惀馃惀馃惀馃惀'],
{ myOtherArray: ['馃馃馃馃'] },
'馃槑馃槑馃槑馃槑',
['馃惀馃惀馃惀馃惀'],
{ myOtherArray: ['馃馃馃馃'] },
'馃槑馃槑馃槑馃槑',
['馃惀馃惀馃惀馃惀'],
{ myOtherArray: ['馃馃馃馃'] },
]

Con este array, la soluci贸n m谩s r谩pida ser铆a la de JSON

Eso s铆, con ninguno de estos m茅todos conseguir铆amos clonar un array que tenga una funci贸n en su estructura, lo puedes ver abajo

js
const myArr = [
'馃槑馃槑馃槑馃槑',
['馃惀馃惀馃惀馃惀'],
{ myOtherArray: ['馃馃馃馃'], myMethod: () => console.log('hola como estamos') },
]

Este m茅todo tiene una funci贸n en la estructura, pues escojas el m茅todo que escojas, esa funci贸n no la podr谩s clonar

馃檵鈥嶁檪锔

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 馃檹
Enviar Feedback 鉁嶏笍
El texto est谩 en blanco!
Gracias por enviarme tu opini贸n
馃憤

Si quieres explorar m谩s cursos y m谩s entradas en el blog, los tienes todos en la p谩gina principal, y si el contenido te ha ayudado si lo compartes me ayudas a dar difusi贸n 馃憤

Privacidad
by kuworking.com
[ 2020 >> kuworking ]