No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Aprende todo un fin de semana sin pagar una suscripción 🔥

Aprende todo un fin de semana sin pagar una suscripción 🔥

Regístrate

Comienza en:

0D
18H
23M
58S

Deep copy con recursividad

9/20
Recursos

Con el Deep Copy podemos generar copias de objetos sin importar que estos posean objetos anidados o métodos dentro.

Aplicando Deep Copy en JavaScript

Veamos el siguiente ejemplo:

// OBJETO ORIGINAL
const studentBase = {
  name: undefined,
  email: undefined,
  age: undefined,
  approvedCourses: undefined,
  learningPaths: undefined,
  socialMedia: {
    twitter: undefined,
    instagram: undefined,
    facebook: undefined,
  },
	hello() {
		 console.log("Hello, World!");
	}
};

// FUNCIÓN RECURSIVA
function isObject(subject) { // Comprueba si es un objeto
  return typeof subject == "object"; // Devuelve true o false
}

function isArray(subject) { // Comprueba si es una Array
  return Array.isArray(subject); // Devuelve true o false
}

// FUNCIÓN RECURSIVA
// Recibe un parametro que puede ser un objeto, array u otro tipo de dato
function deepCopy(subject) {
  let copySubject; // Esta variable se convertira en array, objeto u otro tipo de dato

  const subjectIsObject = isObject(subject); // ¿El parámetro es objeto?
  const subjectIsArray = isArray(subject); // ¿El parámetro es array?

  if (subjectIsArray) { // Si es array...
    copySubject = []; // Asignamos un array vacío donde iremos copiando 1 a 1 los datos
  } else if (subjectIsObject) { // Si es un objeto...
    copySubject = {}; // Asignamosun objeto vacío donde iremos copiando 1 a 1 los atributos
  } else { // Sino es array u objeto...
    // Entonces es un tipo de dato que se puede copiar sin problemas, retornamos dicho
		// dicho dato y terminamos con la ejecución de la fucnción.
		return subject;
  }
	
	// Continuamos con la ejecución de la función si el parámetro fue array u objeto: 
  for (key in subject) { // Recorremos cada uno de los atributos o datos del objeto o array
		// Comprueba si hay un objeto dentro del índice o atributo:
    const keyIsObject = isObject(subject[key]);

    if (keyIsObject) { // Si es verdad que hay un objeto dentro...
	      // Invocamos recursivamente la misma función:
				copySubject[key] = deepCopy(subject[key]); // 👀🔄
    } else { // Sino...
      if (subjectIsArray) { // Si el parámetro recibido por la función deepCopy es Array...
	        // Agregamos el elemento a la variable a retornar al final de la función:
					copySubject.push(subject[key]);
      } else { 
				// sino, significa que es objeto el parámetro y además no hay objetos anidados
				// en el elemento actual dentro del recorrido del bucle for, por tanto, asignamos
				// dicho elemento como valor a la propiedad correspondiente:
        copySubject[key] = subject[key];
      }
    }
  }

  return copySubject; // Finalmente retornamos el objeto/array copia
}

Generemos un objeto copia utilizando la función recursiva e intentemos realizar modificaciones en el objeto copia y original:

// OBJETO COPIA
const juan = deepCopy(studentBase);

// MODIFICACIONES EN EL OBJETO ORIGINAL
studentBase.socialMedia.twitter = "@student_twitter"

// MODIFICACIONES EN EL OBJETO COPIA
juan.socialMedia.instagram = "@juanDC"

// VEAMOS EN CONSOLA LAS DIFERENCIAS DEL OBJETO ORIGINAL Y LA COPIA
console.log(studentBase);
console.log(juan);

/* > Mensaje en consola
{
  name: undefined,
  email: undefined,
  age: undefined,
  approvedCourses: undefined,
  learningPaths: undefined,
  socialMedia: {
    twitter: '@student_twitter', 👈👈 👀
    instagram: undefined,
    facebook: undefined
  },
  hello: [Function: hello] 👈👈 FUNCIÓN
}
{
  name: undefined,
  email: undefined,
  age: undefined,
  approvedCourses: undefined,
  learningPaths: undefined,
  socialMedia: { 
		twitter: undefined, 
		instagram: '@juanDC', 👈👈 👀
		facebook: undefined 
	},
  hello: [Function: hello] 👈👈 FUNCIÓN
}
*/

Podemos notar que los cambios en un objeto no afecta en los valores de las propiedades del otro. Logramos crear una copia de un objeto que no esté condicionada a que si el objeto original tiene objetos anidados o si tiene métodos dentro.

Conozcamos ahora cómo emplear la abstracción en JavaScript con simplemente objetos, es decir, sin utilizar clases. 🤔🚀

Contribución creada por: Martín Álvarez (Platzi Contributor)

Aportes 78

Preguntas 15

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

Hola! Les comparto mi aporte de como entendi el codigo paso por paso:

Funcion bucle compleja pero en recursividad.

let array = [1,23,41,52,42,5,656,6,98]
let numeroArray = 0;

let fun = (numerito) => {
    if( numerito < array.length ) {
    let valorArray = array[numerito];
    console.log(valorArray);
    return fun(numerito + 1)
    } else {
        console.log("fin");
    }  
}

let y = fun(numeroArray);

Deep copy.

Utilizaresmos recursividad para crear copia de los objetos y que no se rompan si tenemos objetso dentro de objetos dentro de objetos, o si tenemos metodos dentro de nuestros objetos.

Bien, deep copy en muchos aspectos es un gran algoritmo con manejo de datos, recursividad, etc etc, por lo tanto ire escribiendo fracciones de codigo y explicandolas, y cua do termine de explicar todo, pondre el algoritmo completo.

const obj1 = {
    a: "a",
    b: "b",
    c: {
       d: "d",
       e: "e", 
    },
    editA() {
        this.a = "AAAAA";
    }
}; 
//lo de aca arriba es el objeto a copiar

function isObject(subject) {
    return typeof subject == "object"
}
function esUnArray(subject) {
    return Array.isArray(subject);
}
//estas 2 funciones son funciones de validacion de datos, estan seran llamads y se les pasaran un parametro, la mayoria de datos se puede validar con typeof, ergo, los arrays son los unicos que tienen un metodo espacial = Array.isArray(objetoAsaberSiEsUnArray)

function deepCopy(subject) {
    let copySubject;
//dentro de sta funcion sucedera todo,el copysubject guardara los datos, este esta esperando a saber si los datos son objetos,arrays u otras cosas como strings
    const subjectIsArray = esUnArray(subject);
    const subjectIsObject = isObject(subject);

//con las constantes subjectIsArray,   subjectIsObject trabajere los datos,  estas son las encargadas de llamar a  las funciones que hicimos fuera de la funcion deepCopy.
  

  if(subjectIsArray) {
        copySubject = [];
    } else if(subjectIsObject) {
        copySubject = {};
    } else {
        return subject;
    }

//por ultimo empezamos a trabajar con los datos ya validados, segun el dato que sea correspondiente, trbajaremos objeto,arrays u otros valores.
//2da parte del algoritmo
for(key in subject) {
//Creamos un bucle for, este bucle (a in b)se puede ejecutar en una estructura de datos como arrays, objetos. Este loop signfica que el elemento a pasara por TODA la estructura de datos de b, y claro, dependieno la posicion de a,este tendra el valor de donde este parado encima. ejemplo: 
//let array = [52,42,56];
//for(a in array) {
	//console.log(array[a]);
//}
        const keyIsObject = isObject(subject[key]);
//con keyIsObject VUELVO a validar si los datos DENTRO de la estructura de datos YA VALIDADA son objetos o datos. 

        if(keyIsObject) {
            copySubject[key] = deepCopy(subject[key]);
// si resulta que son objetos, entonces iremos copiando y pegando los datos en copySubject, y estos datos se iran copiando de manera identica y exitosa gracias la recursividad deepCopy(subject[key]);

} else {
            if(subjectIsArray) {
                copySubject.push(subject[key]);
            } else {
                copySubject[key] = subject[key]
            }
        }
    }
 //estos 2 ultimos casos son mas sencillos ya que simplemente se basa en arrays u elementos que no sean ni arrays ni objetos   
 
    return copySubject;
// Y al final de todo, la funcion debe devolver algo,verdad? en este caso, quien es el que almaceno todos los datos de el objeto que copiamos? el copySubject, bien, ese es quien retornamos.
} 

para mi esta es una de las clases que no entiendes por completo pero que con el tiempo estoy seguro lograr comprender. En conclusión me llevo que copiar objetos en Javascript es un poco laborioso.

Hola compañeros 😀, estuve jugando un poco con el código de la case para entenderlo mejor, y se puede simplificar de esta forma, mas sencillo de leer:

function deepCopy(subject) {
  let copy;

  if (Array.isArray(subject)) {
    copy = [];
  } else if (typeof subject === "object") {
    copy = {};
  } else {
    return subject;
  }

  for (key in subject) {
    copy[key] = deepCopy(subject[key]);
  }

  return copy;
}

Ahora ustedes se preguntaran:
¿Porque en el ciclo for ya no hay validaciones?, esto es debido a que el valor key dentro del for va a corresponder a dos posibles valores: indices de un array, o keys de objetos, dependiendo que sea subject (un objeto o un array) , y como esta misma sintaxis sirve para objetos y arrays podemos usar la misma linea de código, teniendo encuenta que ya antes creamos copy con los valores {} o [] dependiendo si subject era un objeto o array

Jugando un poco con el debugger he visto como la variable let copySubject; empieza a obtener sus elementos
Imagen1
la function deepCopy identifica que es un objecto { } .

Imagen2
los dos primeros elementos ingresan normal: copySubject[key] = subject[key]; //{a,b}.

Imagen3
en el elemento c:{} identifica que hay un objeto .

Imagen4
ingresan de forma normal los elementos copySubject[key] = subject[key]; //{d,e}.

Imagen5
es el momento de ingresar la function editA(){} lo detecta como objeto.

Imagen6
con este ultimo paso, copia todo el contenido de la funcion y para a finalizar la function.

Tomando como inspiración los aportes de @irvingjuarez y @lgfh, les comparto una solución efectiva y más corta:

.

.

P.D. Dejé los statements a la derecha de los if para que no ocupara más líneas y la imagen no fuese más larga.

Deep Copy con recursividad

Se dice que el objeto tiene un Deep Copy cuando cada propiedad del objeto apunta a una copia separada, incluso si la propiedad apunta a un objeto (valores de referencia). Se crea una copia separada del objeto de referencia para el objeto de destino. En caso de copia profunda, las propiedades de referencia del objeto de origen y las propiedades de referencia del objeto de destino apuntan a diferentes ubicaciones de memoria.

Resumen de Deep Copy

  • El objeto de destino contiene una copia separada del nombre, la edad y la designación.
  • Instancias separadas de tipos de valor. Se crea una referencia separada para objetos complejos (“dirección”)

La propiedad “Dirección” de los objetos de origen y destino apuntan a diferentes ubicaciones de memoria. Si el usuario actualiza la propiedad de la dirección en el objeto de origen, las actualizaciones no se reflejan en el objeto de destino.

var userName = {
  name: "Mayank Gupta",
  age: 30,
  designation: "Developer",
  address: {
    street: "Rohini",
    city: "Delhi"
  }
}

Lectura importante!

Deep and Shallow Copy in JavaScript

Código Clase

const obj1 = {
    a: 'a',
    b: 'b',
    c: {
        d: 'd',
        e: 'e',
    },
    editA() {
        this.a = 'Abcd'
    }
};

function isObject(subject) {
    return typeof subject == "object";
}
function isArray(subject) {
    return Array.isArray(subject);
}

function deepCopy(subject) {
    let copySubject;

    const subjectIsObject = isObject(subject)
    const subjectIsArray = isArray(subject)
 
    // inicializamos la variable copySubject segun su tipo de dato
    if (subjectIsArray) {
        copySubject = [];
    } else if (subjectIsObject) {
        copySubject = {};
    } else {
        return subject;
    }

    // Empieza la recursividad
    for (key in subject) {
        const keyIsObject = isObject(subject[key]); // propiedad de nuestro elemento

        if (keyIsObject) {
            copySubject[key] = deepCopy(subject[key]) // Si la propiedad es un objeto, se vuelve a llamar a deepCopy para volver a hacer las asignaciones por cada una de las propiedades de los objetos.
        } else {
            if (subjectIsArray) {
                copySubject.push(subject[key])
            } else {
                copySubject[key] = subject[key]
            }
        }
    }

    return copySubject;
}

// Pruebas en devTools
// const obj2 = deepCopy(obj1);

// obj1
// obj2

// obj1.a = 'AMORMIO'

// obj2.a = 'Si estoy debajo del vaivén de tus piernas'

// obj1
// obj2

Esta clase me hizo volar la cabeza pero todo totalmente comprensible! El mejor profe!

Me parece que hay un momento en el que la clase se hace confusa y es el momento en que el profe decide en el ciclo For In anidar el if de si subjectIsArray, y ademas de la anidación, que utilice subjectIsArray como validación sin pasarle ningun parametro, a mi parecer debió generar un keyIsArray al igual que lo hizo con keyIsObject, y de paso no anidar el if, a que no tiene proposito en si mismo.
les dejo el mismo codigo solo que con unas pequeñas modificaciones a ver que les parece.

const obj1 = {
  a:"b",
  c:"d",
  f:"g",
  capC(prop) {
    this.prop = toUpperCase(this.prop)
  },
  h:"i",
  j:"k",
}

function isObj (elm) {
  return Object.prototype.toString.call(elm) == '[object Object]'
  // return typeof elm == 'object'
}

function isArr (elm) {
  return Array.isArray(elm)
}

function deepCopy (elmToCopy) {
  let copyElm
  
  let itsAnObject = isObj(elmToCopy)
  let itsAnArray = isArr(elmToCopy)
  
  if(itsAnArray)
  {
    copyElm = []
  } 
  else if (itsAnObject)
  {
    copyElm = {}
  } 
  else 
  {
    return elmToCopy
  }
  
  for(subElm in elmToCopy)
  {
    const subElmItsObj = isObj(elmToCopy[subElm])
    const subElmItsArr = isArr (elmToCopy[subElm])
    
    if(subElmItsObj)
    {
      copyElm[subElm] = deepCopy(elmToCopy[subElm])
    } 
    else if (subElmItsArr)
    {
      copyElm.push(elmToCopy[subElm])
    } else {
      copyElm[subElm] = elmToCopy[subElm]
    }
  }
  
  return copyElm
}

let obj2 = deepCopy(obj1)

console.log(obj2)


Aqui hay un error, ya que en la funcion isObject, incluso aunque le pases como argumento un array, te da true. Sin embargo, existe otra forma de validar eso:
.
El función debería quedar así;

function isObject(subject){
  return Object.prototype.toString.call(subject) == "[object Object]";
}

Sabemos si es un array si el string es [object Array]

Una versión acortada y simplificada que estuve probando 😛 :

const deepCopy = elem => {
  if(!isObject(elem)) return elem
  
  const copyOfElem = isArray(elem) ? [] : {}
  
  for(key in elem){
    const current = elem[key]
    
    copyOfElem[key] = isObject(current)
    		? deepCopy(current)
    		: current
  }
  
  return copyOfElem
}

El funcionamiento es el mismo, pero utiliza algunos trucos para acortar espacio

  1. Solo verifica si es un objeto porque los arrays también son objetos, así que solo puede ser o un objeto o un primitivo
  2. Para inicializar la copia como solo tiene 2 opciones (array u objeto) entonces usa una ternaria
  3. Con el for in ya recorre el array así que solo debe verificar si el elemento es un objeto o agregar el elemento en esa posición

Hice algunas pruebas y funcionan igual, pero a lo mejor se me escapa algún edge case, si lo ven me serviría mucho, grax ;D

Tuve que ver el video 3 veces para lograr comprenderlo, pero al fin pude lograrlo, creo que las clases de Juan son de lo mejor y su energía lo es todo 😄

Gracias a las nuevas features de JavaScript😎, hoy tenemos una nueva función que nos puede ayudar para aquellos casos en los que se quiera clonar un objecto o arreglo y no nos baste con un shallow copy 🤔.

La función se llama structuredClone, está permite hacer un deep copy basado en un algoritmo del mismo nombre (Structured Clone) de tal forma que sea mucho mas facil para los devs hacer deep copy de sus estructuras de datos 😮.

Aquí un ejemplo de uso:

const myDeepCopy = structuredClone(myOriginal);

Sin embargo esto es relativamente nuevo al dia de hoy y tiene ciertas limitaciones (como el caso donde se tengan funciones dentro del objeto), si quieren leer más al respecto les dejo un articulo donde lo explican más a detalle 😀.

esta clase si que me mareo tuve que verla varias veces!! es un excelente profe pero este tema me pudo xD a volver a mirar hasta comprender

Holaa, comparto mi solucion a la funcion deepCopy

function typeOfElem(elem) {
  let result;

  switch (Object.prototype.toString.call(elem)) {
    case "[object Object]":
      result = "object";
      break;
    case "[object Array]":
      result = "array";
      break;
    default:
      result = "no problem";
      break;
  }

  return result;
}

function deepCopy(elem) {
  let copy;
  let elemType = typeOfElem(elem);

  if (elemType === "array") {
    copy = [];
  } else if (elemType === "object") {
    copy = {};
  } else {
    return elem;
  }

  for (let item in elem) {
    let elemType2 = typeOfElem(item);

    if (elemType2 === "object") {
      item = deepCopy(item);
    }

    if (elemType === "array") {
      copy.push(item);
    } else {
      copy[item] = elem[item];
    }
  }

  return copy;
}

Maneras de copiar un Array(Deep and shallow)

Existen diferentes maneras de copiar un array, una de ellas es con el método slice(), que nos permite copiar un array sin modificar el original.

var original = [true, true, undefined, false, null];

//slice()
var copy1 = original.slice(0);

//spread operator
var copy2 = [...original];

Estas tienden a no funcionar cuando hay un objeto o un arreglo dentro de un array. Cuando pasa eso necesitas un deep copy
shallow copy:

var deepArray = [["Carlos"]];
var shallowCopy = deepArray.slice(0);

shallowCopy[0].push("es chevere");
console.log(deepArray[0], shallowCopy[0]);
// ["Carlos", "es chevere"]


var deepCopy = JSON.parse(JSON.stringify(deepArray));
deepCopy[0].push("es chevere");
console.log(deepArray[0],deepCopy[0])


Cuando hacemos copy, copiamos el pointer del original array

Nunca me imagine que copiar un Objeto fuese de esta manera tan compleja

Me perdí entre tantos objetos 😦

Acá esta mi aporte, utilice algunas especificaciones de ES6+
Pueden ver el resto de los retos que he resuelto en este y otros cursos de esta misma escuela acá: https://github.com/EdinsonRequena/JsSchool
Espero les sirva de ayuda ❤️

const isObject = subject => typeof subject == 'object'
const isArrayFunc = subject => Array.isArray(subject)

const deepCopy = subject => {
    let copySubject

    const subjectIsObject = isObject(subject)
    const subjectIsArray = isArrayFunc(subject)

    if (subjectIsArray) copySubject = []
    else if (subjectIsObject) copySubject = {}
    else subject

    for (let key in subject) {
        const keyIsObject = isObject(subject[key])
        if (keyIsObject) copySubject[key] = deepCopy(subject[key])
        else subjectIsArray ? copySubject.push(subject[key]) : copySubject[key] = subject[key]
    }

    return copySubject
}

const obj = {
    name: 'Andrea',
    lastName: 'Vinas',
    email: 'andre',
    age: 21,
    objectTest: {
        a: 'a',
        b: 'b',
        1: '1',
    },
    aprovedCourse: ['JavaScript', 'NodeJS', 'ReactJS'],
    addCourse(newCourse) {
        this.aprovedCourse.push(newCourse);
    }
}

const example = deepCopy(obj)

Así fu como lo entendí

//DEEP COPY


    //Funciones para comprobar que el tipo de dato

function isObject(subject){
    return typeof subject == "object"; // recordar que los arrays tambien son de tipo objeto en js
}

function isArray(subject) {
    return Array.isArray(subject);
}

    // Empieza la funcion de copiado profundo

Empieza la funcion de copiado profundo

function deepCopy(subject) {
    let copySubject; // Inicializa la constante que enviara al final

comprueba que la variable enviada sea un array o un objeto

    const subjectIsObject = isObject(subject); // si es array u objeto va a dar true

    const subjectIsArray = isArray(subject); // sólo si es array dara un true

Es necesario que este ciclo if compruebe primero que es un array, ya que como se vio anteriormente los array también son objetos

    if (subjectIsArray) {
        copySubject = []; // transforma la variable inicializada en un array
        console.log("Es array");
    } else if (subjectIsObject) {
        copySubject = {}; // transforma la variable inicializada en un objeto
        console.log("Es objeto");
    } else {
        return subject;
 // en caso de que no sea una variable tipo objeto termina la funcion enviando solo el dato a copia
    }

    for (key in subject) {
        const keyIsObject = isObject(subject[key]); // Comprueba que la Key sea un variable de tipo objeto
        if (keyIsObject) {
            copySubject[key] = deepCopy(subject[key]); // En caso que lo sea llamara la funcion deepCopy de nuevo
        } else {
            if (subjectIsArray) {
                copySubject.push(subject[key]); // Se envia el valor de la Key que no es de tipo objeto (Number, String o boolean) al array copiado
            } else {
                copySubject[key] = subject[key]; // Se evia copia de la key al objeto copiado
            }
        }
    }
    return copySubject;};

`

Les comparto como anoté lo que hace la función deepCopy para mis apuntes:

  1. Recibe como parámetro una variable.
  2. Se verifica si es un objeto, array o otro tipo de variable.
    1. Si es objeto o array entra en un ciclo. Sino retorna la copia de la variable.
  3. Si se entra al ciclo, por cada elemento o atributo del objeto o array
    1. Se verifica nuevamente si es objeto, array o variable.
      1. Si este atributo es un objeto, se llama de nuevo a la misma función. (Acá esta la recursividad). Y se vuelve al primer paso hasta copiar este objeto.
    2. Si es un array se utiliza el método push.
    3. Si es otro tipo de variable solo se hace una asignación normal.
  4. Finalmente el ciclo acaba y se devuelve el array o el objeto copiado.

Deep copy con recursividad

const obj1 = {
  a: 'a',
  b: 'b',
  c: {
    d: 'd',
    e: 'e',
  },
  editA() {
    this.a = 'AAAAA';
  }
};

function isObject(subject) {
  return typeof subject === 'object' && subject !== null;
}

function isArray(subject) {
  return Array.isArray(subject);
}

function deepCopy(subject) {
  let copySubject;

  const subjectIsArray = isArray(subject);
  const subjectIsObject = isObject(subject);

  if (subjectIsArray) {
    copySubject = [];
  } else if (subjectIsObject) {
    copySubject = {};
  } else {
    return subject;
  }

  for(key in subject) {
    const keyIsObject = isObject(subject[key]);

    //*if else - if else
    keyIsObject ? copySubject[key] = deepCopy(subject[key]) : subjectIsArray ? copySubject.push(subject[key]) : copySubject[key] = subject[key];
  }

  return copySubject;
}

deepCopy(obj1);

Excelente clase, cada detalle bien llevado hasta aqui.

// Esta función comprueba si un objeto es válido (no nulo) y es un objeto.
function isObject(obj) {
  return obj !== null && typeof obj === "object";
}

// Esta función comprueba si una variable es un array.
function isArray(arr) {
  return Array.isArray(arr);
}

// Esta es la función principal que se encarga de hacer una copia profunda de un objeto o array.
function deepCopy(subject) {
  // Primero, comprobamos si el sujeto es un objeto o array válido.
  if (isObject(subject)) {
    // Si es un array, creamos una copia vacía.
    // Si no, creamos un objeto vacío.
    const copySubject = isArray(subject) ? [] : {};

    // Usamos Object.entries() para obtener tanto la clave como el valor de cada propiedad.
    for (const [key, value] of Object.entries(subject)) {
      // Comprobamos si el valor de la propiedad es un objeto o un array.
      if (isObject(value)) {
        // Si es un objeto o un array, llamamos a deepCopy() de forma recursiva para copiarlo.
        // Luego asignamos la copia al objeto o array nuevo.
        copySubject[key] = deepCopy(value);
      } else {
        // Si no es un objeto o un array, simplemente copiamos el valor al objeto o array nuevo.
        copySubject[key] = value;
      }
    }

    // Devolvemos el objeto o array nuevo con todas las propiedades copiadas.
    return copySubject;
  } else {
    // Si no es un objeto o array válido, simplemente devolvemos el valor original.
    return subject;
  }
}

En resumen, la función deepCopy realiza una copia profunda de un objeto o array en JavaScript. Primero comprueba si el sujeto es un objeto o array válido. Si es válido, crea un nuevo objeto o array vacío y utiliza un bucle for…of para recorrer todas las propiedades del objeto o array original. Para cada propiedad, comprueba si su valor es un objeto o array. Si lo es, llama a deepCopy de forma recursiva para copiarlo y asigna la copia al objeto o array nuevo. Si no lo es, simplemente copia el valor al objeto o array nuevo. Finalmente, devuelve el objeto o array nuevo con todas las propiedades copiadas. Si el sujeto no es un objeto o array válido, simplemente devuelve el valor original.

Me queda una duda en este punto.
Los arrays dentro de JavaScript también son Objetos, por lo tanto, la validación dentro de la función isObject, también va a retornar true cuando sele pase un array. En consecuencia, se podría ejecutar el if y el else if en el condicional. Debido a que no. La están mandando a salir del condicional cuando es un array.

Hola a todos, les comparto mi código de deep copy.

El código copia todo hasta la capa más profunda, vi que el código de la clase no llega tan profundo, así que le mejoré esa parte.

Les dejo un sreenshot del código y el código como tal.
Todo feedback es bienvenido. 😄

function isObjectOrArray (element) {
	const ELEMENT_IS = {
		'[object Object]' : 'object',
		'[object Array]'  : 'array'
	};
	const TYPE_ELEMENT = Object.prototype.toString.call(element);
	const IS_ELEMENT_ANY = TYPE_ELEMENT !== '[object Object]' && TYPE_ELEMENT !== '[object Array]';
	const RESULT = (IS_ELEMENT_ANY)
		? 'any'
		: ELEMENT_IS[TYPE_ELEMENT];

	return RESULT;
}

function deepCopy (subject) {
	const SUBJECT_IS = isObjectOrArray(subject);

	if (SUBJECT_IS === 'any')
		return subject;
	const COPY_SUBJECT = (SUBJECT_IS === 'array')
		? []
		: {};

	for (const KEY in subject) {
		const IS_KEY_ANY = isObjectOrArray(subject[KEY]) === 'any';

		COPY_SUBJECT[KEY] = (IS_KEY_ANY)
			? subject[KEY]
			: deepCopy(subject[KEY]);
	}

	return COPY_SUBJECT;
}

Esta clase si me exploto la cabeza JAJA.
Pero nada que no sea entendible, solamente es de repasarlo y practicar!

Una parte de mi se emociona por el desafio que representaria usar una funcion recursiva sin generar un “Stack Overflow”.
Además, pienso que al momento de implementar dicha funcionalidad el código sería más escalable. En cuanto a proyectos grandes.

Toda la clase o el tema estuvo muuuy loco y me volo la cabeza una parte en especial.
este fue mi objeto de prueba:

const obj1 = {
  a: "a",
  b: "b",
  c: {
    d: "d",
    e: "e"
  },
  f: ["♑️", "💀", "☯️", "💻"],
  g: () => {
    console.log("hi i'm a method in 'g'");
  },
  h: [1,2,3,{cuatro: '4', editA: () =>{this.a = 'AAAAA'}}]
};

Como pueden ver tiene arrays dentro de sus propiedades y a la vez arrays con objetos como elementos.
Mi problema principalmente era que no entendía cómo validamos que una de las propiedades fuera un array, si solamente estamos validando con isObject(subject[prop]) y no con isArray… Luego todo cobro sentido cuando recorde que un array (así como muchas cosas en JS) es un objeto:

Noten que las dos validaciones de isArray e isObject son true en mi subject que es un array dentro de mi objeto. Asi que un Array([ ]) es un Array(obviamente) y es un objeto(al menos en nuestra validación con typeof , pero un objeto ({ })es un objeto(obviamente) y no es un array.

Bueno al menos esto fue lo que deduje y entendí, si estoy equivocado en mi razonamiento diganme para no vivir engañado otra vez XD.

La mejor forma para entender esta clase es usando debugger en el navegador es lo máximo

Está muy buena la clase y más que todo para hacer el ejercicio 👏, pero hoy en día ya tenemos una forma nativa de como hacer copias profundas en JS.

Por si no lo conocían se llama structureClone aquí les paso la doc por si la necesitan. https://developer.mozilla.org/en-US/docs/Web/API/structuredClone

Pero ojo con el soporte para navegadores antiguos
https://caniuse.com/?search=structuredClone

Una Deep Copy establece una copia de los elementos que forman parte del objeto, pero omitiendo la copia de sus referencias. Es decir, creando un nuevo objeto en memoria, copiando o clonando su original, lo que representa un snapshoot del objeto en sí mismo en un momento dado.

Hola!!, llegue un año tarde, pero este seria una propuesta para lo hecho, no se que tan bien esta, pero lo interesante es que funciona

const obj1 = {
    a: 'a',
    b: 'b',
    c: {
        d: 'd',
        e: 'e'
    },
    editA() {
        this.a = 'AAAA'
    }
}

const elements = {
    "[Object Object]": {},
    "[Object Array]": []
}

function typeOfElement(subject) {return Object.prototype.toString.call(subject)}

function deepCopy(subject){
    let copySubject;
    
    const subjectIsAnElement = typeOfElement(subject);
    
    copySubject = elements[subjectIsAnElement] || subject
    
    for(let key in subject){
        const keyIsElement = typeOfElement(subject[key]);
        
        if(keyIsElement.includes('Object')) copySubject[key] = deepCopy(subject[key])

        keyIsElement.includes('Array') 
        ? copySubject.push(subject[key]) 
        : copySubject[key] = subject[key]
    }
    return copySubject;
}

deep copy copiado de objetos sin errores

Cabe destacar algo aquí y es que chequear tipos con typeof no es la mejor manera de hacer las cosas en JS.
.
Recordemos que en Javascript todo es un objeto y a veces un typeof === 'object' puede no ser todo lo confiable que esperarías, por ejemplo:

typeof null === 'object' // true
typeof [] === 'object' // true

Hay muchos casos en donde este tipo de comprobaciones no son de fiar, es preferible muchas veces ingeniarselas un poquito y pensar más allá… por ejemplo ¿Que es lo que todo objeto literal tiene? Pues, keys. Entonces:

if (Object.keys(subjectIsObject).length) {
  ...
}

Lo anterior sería una solución básicamente infalible, si el item devuelve un array, entonces ya sabes que es un literal.

Este sería un buen ejercicio de leetcode 😃

Costo un poco pero al final salió, acá dejo una solución
que hice antes de ver la clase como reto!

function recursiveCopy (src,target){    
  
    for(props in src){
        let aux;
        if( isObject(src[props]) && !isArray(src[props])){
            aux = props;
            target[aux] = {};
            recursiveCopy(src[props], target[aux]);
        }else if(isArray(src[props])){
            aux = props;
            target[aux] = [];
            for(index of src[aux]){
                recursiveCopy(src[props],target[aux]);
            }
        }else{
            target[props] = src[props] 
        }       
    }
}

function isObject(prop){    
    return typeof prop === "object";
}

function isArray(prop){
    return Array.isArray(prop);
}
//esto es declarar la variable
let copySubject;

//Esto es inicializarla
copySubject = "cualquierValor";

Tuve que releer el código del bucle un par de veces, pero finalmente logré entenderlo 😄

Tenia la duda si dentro del obj1 hay un Array, y luego de hacer el const obj2 = deepCopy(obj1), trato de modificar el Array del obj1 agregando con push un valor, al probar en console, no se va a modificar el obj2 En Resumen: funciona tambien si dentro de mi objeto tengo Arrays

Aqui dejo mi versión refactorizada de deepCopy:

function deepCopy(subject) {
  if (typeof subject !== 'object') return subject;
  const copySubject = Array.isArray(subject) ? [] : {};
  Object.keys(subject).forEach(key => {
    copySubject[key] = deepCopy(subject[key]);
  })
  return copySubject;
}

me hizo recordar cuando en la universidad nos pidieron elaborar un programa que le pegaras un bloque de codigo y te dijera que lenguaje era cuantas variables se habian creado y si existian condicionales y cuantos eran… analizar linea por linea los texto, en este caso analizando cada loop del for in

Debugeado a mano para quienes no sepan que esta pasando a la perfeccion ❤️

function deepCopy(subject) {
    let copySubject;

    const subjectIsObject = isObject(subject);
    const subjectIsArray = isArray(subject);

    if (subjectIsArray) 
    {
        copySubject = [];
        console.log("Inicializammos en ", copySubject);
    } 
    
    else if (subjectIsObject) 
    {
        copySubject = {};
        console.log("Inicializamos en ", copySubject);
    } 
    else 
    {
        return subject;
    }

    for (key in subject) 
    {
        const keyIsObject = isObject(subject[key]);

        if (keyIsObject) 
        {
            copySubject[key] = deepCopy(subject[key]);
            console.log(copySubject, "BandObject");
        } 
        
        else 
        {
            if (subjectIsArray) 
            {
                copySubject.push(subject[key]);
                console.log(`push of ${subject[key]} : BandArray`);
            } 

            else 
            {
                copySubject[key] = subject[key];
                console.log(copySubject, " BandNormie");
            }
        }
    }

    return copySubject;
}

Si ven algun error agradezco de sus comentarios

Simplificándolo un poco, el código quedaría así:

function deepCopy(element) {
  let copy;
  
  if (Array.isArray(element)) copy = [];
  else if (typeof element === "object") copy = {};
  else return element;
  
  for (item in element) {
    copy[item] = deepCopy(element[item]); 
  }
  return copy;
}

En ese caso yo lo hago asi… estoy en el 2030

//objeto ejemplo
const original= {
    name: "Pedrito",
    lastName: "Roca",
    personalinformation: {
       date: "11-11-2222",
       phone: 1122334455, 
    },
    editA() {
        this.name = "Pietro";
    },
    unascosas:[1,2,3,{a:"holaa", b:"lalalsl", c:23},5,6]
}; 

//metodo ultrasuperfantastico para copiar
const copia = {...original};

console.log(original);
console.log(copia);

Mi propuesta del algoritmo

function miDeepCopy(subject){
    let copySubject;
    if (typeof subject === 'object' && subject !== null) {
        copySubject = {};
    } else if(Array.isArray(subject)){
        copySubject = [];
    }else{
        return subject;
    }

    for(let i in subject){
        if(typeof subject[i] === 'object' && subject[i] !== null){
            copySubject[i] = miDeepCopy(subject[i]);
        }else if(Array.isArray(subject[i])){ //if it is an array
            copySubject[i] = subject[i].slice();
        }else{
            copySubject[i] = subject[i];
        }
    }
    return copySubject;
}

me siento satisfecho de aprender a la primera esto, puedo mimir en paz hoy

a pesar que tuve que repetir la clase un par de veces, y detenerme en la explicacion y reflexionar lo que decia el profe, es gratificante poder entender el codigo, y asi mejorar nuestra comunicacion con nuestra maquina, muy satisfecho con esta clase.


//Original object
const obj1 = {
    a: "a",
    b: "b",
    c: {
        d: "d",
        e: "e",
    },
    // f: ["apple", "banana"],
    editA() {
        this.a = "aaaa";
    }
};

//----------- Recursion. Recursive function

//Validators
function isObject(subject) {
    return typeof subject == "object";
}

function isArray(subject) {
    return Array.isArray(subject);
}

//DeepCopy function
function deepCopy(subject) {
    let copySubject;

    //Validator inside the function
    const subjectIsObject = isObject(subject);
    const subjectIsArray = isArray(subject);

    //Initialize the variable copySubject 
    if(subjectIsObject) {
        copySubject = {};
    } else if (subjectIsArray) {
        copySubject = [];
    } else {
        return subject;
    }

    //For loop
    for (key in subject) {
        //Check if the key is an object
        const keyObject = isObject(subject[key]);
        const keyArray = isArray(subject[key]);
        //Make conditionals. Object, array, something else.
        if(keyObject) {
            copySubject[key] = deepCopy(subject[key]);
        } else {
            if(keyArray) {
                copySubject.push(subject[key]);
            } else {
                copySubject[key] = subject[key];
            }
        }
    }
    return copySubject;
}

Algo curioso que note jugando con la funcion ‘isObject’ es que devuelve ‘true’ con los arrays tambien, incluso haciendo mas estricta la comparacion con ===

function isObject(subject) {
  return typeof(subject) == 'object';
}
const object = {};
const array = [];

console.log(isObject(array))  // true

Esta clase podría llegar a ser un poco confusa si no se tienen bien los fundamentos, básicamente estamos reduciendo los objetos a datos primitivos los cuales guardan el valor y no la referencia en memoria, por lo cual al copiarlos estamos copiando el valor que tienen guardado.

Yo simplemente hubiese usado el desestructurador de objetos y sha, jaja

Pero entiendo que la recursividad era par dar un ejemplo de su uso

const obj1 = {
  a: "a",
  b: "b",
  c: {
     d: "d",
     e: "e", 
  },
  editA() {
      this.a = "AAAAA";
  }
};
function isArray (subject) { return Array.isArray(subject) };
function isObject (subject) { return typeof subject == "object" };

function deepCopy(subject) {
  let copySubject;
  const subjectIsArray = isArray(subject);
  const subjectIsObject = isObject(subject);

  if(subjectIsArray) copySubject = [];
  else if(subjectIsObject) copySubject = {};
  else return subject;

  for( key in subject ) {
    const keyIsObject = isObject(subject[key]);
    if(keyIsObject) {
      copySubject[key] = deepCopy(subject[key]);
    } else {
      if(subjectIsArray) {
        copySubject.push(subject[key]);
      } else {
        copySubject[key] = subject[key];
      }
    }
  }

  return copySubject;
}  

Les dejo mis apuntes de la clase donde tambien revisar mis apuntes del curso en gitbook:
teniendo como base el siguiente objeto con metodos vamosa ir aplicando la recursividad con el deep Copy:

const obj1 = {    a: "a",    b: "b",    c: {        d: "d",        e: "e",    },    editA() {        this.a = "AAAAAA";    }};

Validaremos si nuestro objeto de prueba es un objeto o si es un array, en el uno utilizaremos la palabra reservada typeof que nos permite validar si nuesto objetodePrueba es un objeto o lo que le pidamos, y con el array llamamos al superprototypoArray y luego a su metodo estatico isArray.

function isObject(subject) {    return typeof subject == "object";}
function isArray(subject) {    return Array.isArray(subject);}

Para hacer el deepcopy vamos a validar si es un objeto o un array y de acuerdo a eso validaremos y daremos una respuesta de copiado según sea un objeto o un array

function deepCopy(subject) {    
	let copySubject;
        const subjectIsObject = isObject(subject);
	const subjectIsArray = isArray(subject);
        if (subjectIsArray) {
        copySubject = [];
	} else if (subjectIsObject) {        
	copySubject = {};
    	} else {
        return subject;
    }        for (key in subject) {
        const keyIsObject = isObject(subject[key]);
        if (keyIsObject) {
            copySubject[key] = deepCopy(subject[key])
        } else {
            if (keyIsObject) { 
                 copySubject[key] = deepCopy(subject[key]);
	} else {
                if (subjectIsArray) {
                    copySubject.push(subject[key]);
                } else {
                    copySubject[key] = subject[key];
                }
            }
        }
    }        return copySubject;
}

De esta forma iteramos la copia de nuestros objetos por cada uno de sus valores, ya tengan otros objetos u otros arrays dentro de ellos, incluyendo a sus métodos.

Excelente clase 😀

.

Me parece que JavaScript acaba de implementar un método para hacer un deep copy de un objeto: structuredClone.

https://developer.mozilla.org/en-US/docs/Web/API/structuredClone

Hola, esta es mi versión del deepcopy, espero les guste y puedan ahcer correcciones al código

const obj1 = {
  a: {
    a1: "a",
    a2: "a",
  },
  b: "b",
  edita() {
    this.a.a1 = "aaaaaa";
  },
};

let obj2 = {};

const isObject = (intro) => {
  return typeof intro === "object" ? true : false;
};

const isArray = (intro) => Array.isArray(intro);

const deepCopy = function (objecToCopy, pasteIn) {
  if (isObject(objecToCopy)) {
    pasteIn = {};
  } else if (isArray(objecToCopy)) {
    pasteIn = [];
  } else {
    return objecToCopy;
  }

  for (let item in objecToCopy) {
    if (isObject(objecToCopy[item])) {
      pasteIn[item] = deepCopy(objecToCopy[item], pasteIn[item]);
    } else if (isArray(objecToCopy[item])) {
      pasteIn.push(objecToCopy[item]);
    } else {
      pasteIn[item] = objecToCopy[item];
    }
  }
  return pasteIn;
};

console.log(deepCopy(obj1, obj2));

Muy complicado no entendí nada.

Creo que lo que hace confuso la clase es que es muy teorico, y no vemos un resultado hasta el final. Entre toda la explicacion y lineas nos perdemos, son 14 minutos seguidos de codigo sin ver resultado.

Esta clase es la clase para ver varias veces, leer todos los comentarios, leer las preguntas y respuestas y aun así tener la sensación que algo falta como un vacío que no se comprende.
Buena clase

juan ayuda!

Creo que encontré un problema con la funcion deepCopy

function deepCopy(subject) {
  let copySubject;

  const subjectIsObject = isObject(subject);
  const subjectIsArray = isArray(subject);

  if (subjectIsArray) {
    copySubject = [];
  } else if (subjectIsObject) {
    copySubject = {};
  } else {
    return subject;
  }

  for (let key in subject) {
    const keyIsObject = isObject(subject[key]);

    if (keyIsObject) {
      copySubject[key] = deepCopy(subject[key]);
    } else {
      if (subjectIsArray) {
        copySubject.push(subject[key]);
      } else {
        copySubject[key] = subject[key];
      }
    }
  }

  return copySubject;
}```

Este es el codigo que se hizo en la clase ^


Quiero resaltar que durante la siguiente validación tenemos un problema de logica

for (let key in subject) {
const keyIsObject = isObject(subject[key]);

if (keyIsObject) {
  copySubject[key] = deepCopy(subject[key]);
} else {
  if (subjectIsArray) {
    copySubject.push(subject[key]);
  } else {
    copySubject[key] = subject[key];
  }
}

}```

Si se dan cuenta cada vez que se ejecute este codigo

if (keyIsObject) {
      copySubject[key] = deepCopy(subject[key]);
    } else {
      if (subjectIsArray) {
        copySubject.push(subject[key]);
      } else {
        copySubject[key] = subject[key];
      }

Siempre tendremos como resultado que el key que estemos evaluando sera un instanceof object, la razon es porque los arrays son instacias del prototipo object, entonces la validacion
" if (subjectIsArray)" nunca sera realmente util

me di cuenta de este problem comprobando la efectividad de copiar arrays con metodos como push, el caso es que si la validacion " if (subjectIsArray)" funcionara probablemente la funcion deepCopy estaria rota, porque solo haria una shallow copy de los arrays, la razon por la que funciona correctamente en este momento es porque se aplica la recursividad cuando se valida si es un instanceof object con el metodo isObject cuyo return instanciamos en keyIsObject

en resumen, el codigo funciona bien pero hay un error y si no usáramos las validaciones correctas terminaríamos con shallow copies de los arrays

bajo el mismo contexto, no es necesario que mantengamos esa validación, si lo removemos del codigo igual funciona bien

ejemplo :

function deepCopy(subject) {
  let copySubject;

  const subjectIsObject = isObject(subject);
  const subjectIsArray = isArray(subject);

  if (subjectIsArray) {
    copySubject = [];
  } else if (subjectIsObject) {
    copySubject = {};
  } else {
    return subject;
  }

  for (let key in subject) {
    const keyIsObject = isObject(subject[key]);

    if (keyIsObject) {
      copySubject[key] = deepCopy(subject[key]);
    } else {
      copySubject[key] = subject[key];
    }
  }

  return copySubject;
}```



<code>



Si les cuesta entenderlo hagan la lógica en una libreta con pseudocódigo o diagrama, vean el video y replíquenlo en la libreta sin ver el código de Juan al inicio cuesta pero les asegura que se si le dedican tiempo y paciencia terminaran haciendo hasta su propia solución.

hoy siento que no podría hacer esto solo ni de broma, pero espero volver a ver este comentario en un tiempo más y reirme de mí mismo.

No entendí ni 🐥. Me toca volver a ver la clase.

Me cuesta entender la recursividad ya que es un concepto realmente difícil de comprender por esta razón estuve realizando búsquedas para de como funciona y encontré este video que me ayudo a comprender un poco mas la recursividad, pero espero en mi futuro tener mas claro este concepto para saber cuando es una buena opción usarlo.

Hay un bug en la funcion isObject, la comparacion devuelve true cuando el argumento es un objeto, pero cuando el argumento es un array devuelve tambien true 😀

No hay caso. No puedo hacer que funcione. Al modificar un objeto se modifica también el otro, incluso copiando el codigo que está en recursos.

Hola!! encontre un pequeño problema, ahora vere como resolverlo pero queria comentarlo aca para ver las soluciones que implementaron, Cuando ejecuto la función isObject

function isObject(subject) {
  return typeof subject == "object";
}

Y pasó un array ejemplo let a = [1,2,5,7,89,23]; , esta tambien me retorna que es true!

Muchachos ya hay una manera mas fácil de copiar obj, sin la referencia en memoria, sino creando un nuevo obj.
Esto sin hacer todo lo que el profe dijo:

const obj1 = {
    a: 'a',
    b: {
        c: 'c',
        d: 'd'
    },
    saludar(){
        return this.a
    }
}

//Spreed Operator
const obj2 = {...obj1} ;

obj2.a = 'Este es obj2'

console.log('obj1', obj1)
console.log('obj2', obj2)
console.log(obj2.saludar());

Exelente clase, quedó mucho más claro el concepto introducido en la clase anterior.

UIff!! como el nombre de la clase mismo lo dice, vaya que esto si estuvo profundo y enredado de entender, como al minuto 6" ya me perdí🙂😥

Estuve buscando en que momento se usaba el spread operator, asi que el spread operator seria un deep copy,

rayos, el profe siempre se equivoca con lo de declarar e inicializar xD

esta clase ha sido bien compleja…

que buena clase, es complejho pero está muy bien explicado y se hace más fácil. Encontré un pequeño error: la función isObject devuelve true cuando le pasamos un array. Lo solucioné para la función deepCopy cambiando un poco la valdiación:

function deepCopy (subject) {
    let copySubject;
    if (isObject(subject) && !isArray(subject)) { //si no es un array pero si es un objeto
        copySubject = {};
        for (key in subject) {
           copySubject[key] = deepCopy(subject[key]) 
        }
    } else if (isArray(subject)) {
        copySubject = [];
        for (key in subject) {
            copySubject.push(subject[key]);
        }
    } else {
        copySubject = subject
    }
    return copySubject
}

Seguro hay mejores maneras de hacerlo, espero que a alguien le sirva igual