unir referencia objetos objeto copiar clonar array javascript

referencia - javascript unir dos objetos



¿Cómo clonar una matriz de objetos en Javascript? (30)

... donde cada objeto también tiene referencias a otros objetos dentro de la misma matriz?

Cuando se me ocurrió por primera vez este problema, simplemente pensé en algo como

var clonedNodesArray = nodesArray.clone()

existiría y buscaría información sobre cómo clonar objetos en javascript. Encontré una question en StackOverflow (respondida por el mismo @JohnResig) y él señaló que con jQuery podrías hacer

var clonedNodesArray = jQuery.extend({}, nodesArray);

para clonar un objeto. Intenté esto, sin embargo, esto solo copia las referencias de los objetos en la matriz. Así que si yo

nodesArray[0].value = "red" clonedNodesArray[0].value = "green"

el valor de nodesArray [0] y clonedNodesArray [0] será "verde". Entonces lo intenté

var clonedNodesArray = jQuery.extend(true, {}, nodesArray);

que copia en profundidad un Objeto, pero recibí mensajes de " demasiada recursión " y " control de desbordamiento de pila " tanto de Firebug como de Opera Dragonfly respectivamente.

¿Como lo harias? ¿Es esto algo que ni siquiera debería hacerse? ¿Hay una forma reutilizable de hacer esto en Javascript?



Array.slice se puede usar para copiar una matriz o parte de una matriz. http://www.devguru.com/Technologies/Ecmascript/Quickref/Slice.html Esto funcionaría con cadenas y números. Cambiando una cadena en. una matriz no afectaría a la otra, pero los objetos todavía se copian por referencia, por lo que los cambios a los objetos de referencia en una matriz tendrán un efecto en la otra matriz.

Aquí hay un ejemplo de un administrador de deshacer de JavaScript que podría ser útil para esto: http://www.ridgway.co.za/archive/2007/11/07/simple-javascript-undo-manager-for-dtos.aspx


Como mencionó Daniel Lew, los gráficos cíclicos tienen algunos problemas. Si tuviera este problema, agregaría métodos clone() especiales a los objetos problemáticos o recordaría los objetos que ya había copiado.

Lo haría con un copyCount variable que aumenta en 1 cada vez que copia en su código. Se copia un objeto que tiene un copyCount más copyCount que el proceso de copia actual. Si no, la copia, que ya existe, debe ser referenciada. Esto hace que sea necesario vincular desde el original a su copia.

Todavía hay un problema: la memoria. Si tiene esta referencia de un objeto a otro, es probable que el navegador no pueda liberar esos objetos, ya que siempre se hace referencia a ellos desde algún lugar. Tendrías que hacer una segunda pasada donde estableciste todas las referencias de copia en Null. (Si haces esto, no tendrías que tener un copyCount pero un isCopied booleano sería suficiente, ya que puedes restablecer el valor en la segunda pasada).


Creo que logré escribir un método genérico de clonación profunda de cualquier estructura de JavaScript, principalmente utilizando Object.create que es compatible con todos los navegadores modernos. El código es así:

function deepClone (item) { if (Array.isArray(item)) { var newArr = []; for (var i = item.length; i-- !== 0;) { newArr[i] = deepClone(item[i]); } return newArr; } else if (typeof item === ''function'') { eval(''var temp = ''+ item.toString()); return temp; } else if (typeof item === ''object'') return Object.create(item); else return item; }


Dependiendo de si tiene un guión bajo o Babel, aquí hay un punto de referencia de las diferentes formas de clonar en profundidad una matriz.

https://jsperf.com/object-rest-spread-vs-clone/2

Parece que Babel es el más rápido.

var x = babel({}, obj)


El problema con su copia superficial es que todos los objetos no están clonados. Si bien las referencias a cada objeto son únicas en cada matriz, una vez que finalmente te agarras, tratas con el mismo objeto que antes. No hay nada de malo en la forma en que lo clonó ... el mismo resultado se produciría utilizando Array.slice ().

La razón por la que su copia profunda está teniendo problemas es porque está terminando con referencias de objetos circulares. Profundo irá tan profundo como pueda, y si tienes un círculo, continuará infinitamente hasta que el navegador se desmaye.

Si la estructura de datos no puede representarse como un gráfico acíclico dirigido, no estoy seguro de que pueda encontrar un método de uso múltiple para la clonación profunda. Los gráficos cíclicos brindan muchos casos complicados, y como no es una operación común, dudo que alguien haya escrito una solución completa (si es posible, podría no serlo. Pero no tengo tiempo para tratar de escribir una prueba rigurosa ahora). He encontrado algunos buenos comentarios sobre el tema en esta página .

Si necesita una copia en profundidad de un Array of Objects con referencias circulares, creo que tendrá que codificar su propio método para manejar su estructura de datos especializada, de modo que sea un clon de múltiples pases:

  1. En la primera ronda, haga un clon de todos los objetos que no hacen referencia a otros objetos en la matriz. Mantenga un seguimiento de los orígenes de cada objeto.
  2. En la segunda ronda, une los objetos juntos.

El siguiente código realizará recursivamente una copia profunda de los objetos y la matriz :

function deepCopy(obj) { if (Object.prototype.toString.call(obj) === ''[object Array]'') { var out = [], i = 0, len = obj.length; for ( ; i < len; i++ ) { out[i] = arguments.callee(obj[i]); } return out; } if (typeof obj === ''object'') { var out = {}, i; for ( i in obj ) { out[i] = arguments.callee(obj[i]); } return out; } return obj; }

Fuente


Es posible que tenga una forma sencilla de hacerlo sin tener que hacer una recursión dolorosa y sin conocer todos los detalles más finos del objeto en cuestión. Usando jQuery, simplemente convierte tu objeto a JSON usando jQuery $.toJSON(myObjectArray) , luego toma tu cadena JSON y $.toJSON(myObjectArray) nuevo a un objeto. BAM! ¡Hecho y hecho! Problema resuelto. :)

var oldObjArray = [{ Something: ''blah'', Cool: true }]; var newObjArray = eval($.toJSON(oldObjArray));


Estaba bastante frustrado por este problema. Aparentemente, el problema surge cuando envía una matriz genérica al método $ .extend. Entonces, para solucionarlo, agregué un pequeño cheque, y funciona perfectamente con arreglos genéricos, arreglos jQuery y cualquier objeto.

jQuery.extend({ deepclone: function(objThing) { // return jQuery.extend(true, {}, objThing); /// Fix for arrays, without this, arrays passed in are returned as OBJECTS! WTF?!?! if ( jQuery.isArray(objThing) ) { return jQuery.makeArray( jQuery.deepclone($(objThing)) ); } return jQuery.extend(true, {}, objThing); }, });

Invocar utilizando:

var arrNewArrayClone = jQuery.deepclone(arrOriginalArray); // Or more simply/commonly var arrNewArrayClone = $.deepclone(arrOriginalArray);


Este método es muy simple y puede modificar su clon sin modificar la matriz original.

// Original Array let array = [{name: ''Rafael''}, {name: ''Matheus''}]; // Cloning Array let clone = array.map(a => {return {...a}}) // Editing the cloned array clone[1].name = ''Carlos''; console.log(''array'', array) // [{name: ''Rafael''}, {name: ''Matheus''}] console.log(''clone'', clone) // [{name: ''Rafael''}, {name: ''Carlos''}]


Esto copia en profundidad matrices, objetos, valores nulos y otros valores escalares, y también copia en profundidad cualquier propiedad en funciones no nativas (lo cual es bastante raro pero posible). (Para mayor eficiencia, no intentamos copiar propiedades no numéricas en arreglos).

function deepClone (item) { if (Array.isArray(item)) { var newArr = []; for (var i = item.length; i-- > 0;) { newArr[i] = deepClone(item[i]); } return newArr; } if (typeof item === ''function'' && !(//(/) /{ /[native/).test(item.toString())) { var obj; eval(''obj = ''+ item.toString()); for (var k in item) { obj[k] = deepClone(item[k]); } return obj; } if (item && typeof item === ''object'') { var obj = {}; for (var k in item) { obj[k] = deepClone(item[k]); } return obj; } return item; }


Esto funciona para mí:

var clonedArray = $.map(originalArray, function (obj) { return $.extend({}, obj); });

Y si necesita una copia en profundidad de los objetos en la matriz:

var clonedArray = $.map(originalArray, function (obj) { return $.extend(true, {}, obj); });


Estoy respondiendo esta pregunta porque no parece haber una solución simple y explícita para el problema de "clonar una matriz de objetos en Javascript":

function deepCopy (arr) { var out = []; for (var i = 0, len = arr.length; i < len; i++) { var item = arr[i]; var obj = {}; for (var k in item) { obj[k] = item[k]; } out.push(obj); } return out; } // test case var original = [ {''a'' : 1}, {''b'' : 2} ]; var copy = deepCopy(original); // change value in copy copy[0][''a''] = ''not 1''; // original[0][''a''] still equals 1

Esta solución itera los valores de la matriz, luego itera las claves del objeto, guardando las últimas en un nuevo objeto y luego empujando ese nuevo objeto a una nueva matriz.

Ver jsfiddle . Nota: un simple .slice() o [].concat() no es suficiente para los objetos dentro de la matriz.


JQuery extend está funcionando bien, solo necesita especificar que está clonando una matriz en lugar de un objeto ( tenga en cuenta que [] en lugar de {} como parámetro del método extend ):

var clonedNodesArray = jQuery.extend([], nodesArray);


La mejor y más actualizada forma de hacer este clon es la siguiente:

Usando el operador de propagación ES6 "..."

Aquí está el ejemplo más simple:

var clonedObjArray = [...oldObjArray];

De esta manera, distribuimos la matriz en valores individuales y la colocamos en una nueva matriz con el operador [].

Aquí hay un ejemplo más largo que muestra las diferentes formas en que funciona:

let objArray = [ {a:1} , {b:2} ]; let refArray = objArray; // this will just point to the objArray let clonedArray = [...objArray]; // will clone the array console.log( "before:" ); console.log( "obj array" , objArray ); console.log( "ref array" , refArray ); console.log( "cloned array" , clonedArray ); objArray[0] = {c:3}; console.log( "after:" ); console.log( "obj array" , objArray ); // [ {c:3} , {b:2} ] console.log( "ref array" , refArray ); // [ {c:3} , {b:2} ] console.log( "cloned array" , clonedArray ); // [ {a:1} , {b:2} ]


Mi acercamiento:

var temp = { arr : originalArray }; var obj = $.extend(true, {}, temp); return obj.arr;

me da un clon agradable, limpio y profundo de la matriz original, sin que ninguno de los objetos haga referencia al original :-)


Para clonar los objetos también iba a sugerir ECMAScript 6 reduce() :

const newArray=myArray.reduce((array, element)=>array.push(Object.assign({}, element)), []);

Pero francamente me gusta la respuesta de @dinodsaurus incluso mejor. Solo estoy poniendo esta versión aquí como otra opción, pero personalmente usaré map() como lo sugiere @dinodsaurus.


Podemos inventar un método de matriz recursiva simple para clonar matrices multidimensionales. Mientras que los objetos dentro de las matrices anidadas mantienen su referencia a los objetos correspondientes en la matriz de origen, las matrices no lo harán.

Array.prototype.clone = function(){ return this.map(e => Array.isArray(e) ? e.clone() : e); }; var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ], brr = arr.clone(); brr[4][2][1] = "two"; console.log(JSON.stringify(arr)); console.log(JSON.stringify(brr));


Si todo lo que necesita es una copia superficial, una manera realmente fácil es:

new_array = old_array.slice(0);


Simplemente clona cualquier tipo de matriz con:

[].concat(data);

o, dado que es posible que concat no funcione en algunos navegadores IE, puede usar esto:

data.slice(0);


Uso el nuevo método Object.assign de ECMAScript 6:

let oldObject = [1,3,5,"test"]; let newObject = Object.assign({}, oldObject);

El primer argumento de este método es la matriz que se actualizará. Pasamos un objeto vacío porque queremos tener un objeto nuevo.

También podemos usar esta sintaxis, que es la misma pero más corta:

let newObject = [...oldObject];


Vamos, muchachos, es el siglo XXI: mientras sus objetos contengan contenido serializable JSON (sin funciones, sin Number.POSITIVE_INFINITY , Number.POSITIVE_INFINITY , etc.) no hay necesidad de realizar bucles para clonar matrices u objetos. Aquí hay una solución de una línea de vainilla pura.

var clonedArray = JSON.parse(JSON.stringify(nodesArray))

Para resumir los comentarios a continuación, la principal ventaja de este enfoque es que también clona el contenido de la matriz, no solo la matriz en sí. Las desventajas principales son su límite de solo trabajar en contenido serializable JSON y su rendimiento (que es significativamente peor que un enfoque basado en slice ).


con jQuery:

var target= []; $.each(source, function() {target.push( $.extend({},this));});


lodash tiene la función cloneDeep para este propósito:

var objects = [{ ''a'': 1 }, { ''b'': 2 }]; var deep = _.cloneDeep(objects);


olvide eval () (es la característica más mal utilizada de JS y hace que el código sea lento) y slice (0) (funciona solo para tipos de datos simples)

Esta es la mejor solución para mí:

Object.prototype.clone = function() { var myObj = (this instanceof Array) ? [] : {}; for (i in this) { if (i != ''clone'') { if (this[i] && typeof this[i] == "object") { myObj[i] = this[i].clone(); } else myObj[i] = this[i]; } } return myObj; };


Map creará una nueva matriz a partir de la antigua (sin referencia a la anterior) y dentro de la misma, creará un nuevo objeto e iterará sobre las properties (claves) y asignará valores del antiguo objeto de la matriz a las propiedades correspondientes al nuevo objeto.

Esto creará exactamente la misma matriz de objetos.

let newArray = oldArray.map(a => { let newObject = {}; Object.keys(a).forEach(propertyKey => { newObject[propertyKey] = a[propertyKey]; }); return newObject ; });



var game_popularity = [ { game: "fruit ninja", popularity: 78 }, { game: "road runner", popularity: 20 }, { game: "maze runner", popularity: 40 }, { game: "ludo", popularity: 75 }, { game: "temple runner", popularity: 86 } ]; console.log("sorted original array before clonning"); game_popularity.sort((a, b) => a.popularity < b.popularity); console.log(game_popularity); console.log("clone using object assign"); const cl2 = game_popularity.map(a => Object.assign({}, a)); cl2[1].game = "clash of titan"; cl2.push({ game: "logan", popularity: 57 }); console.log(cl2); //adding new array element doesnt reflect in original array console.log("clone using concat"); var ph = [] var cl = ph.concat(game_popularity); //copied by reference ? cl[0].game = "rise of civilization"; game_popularity[0].game = ''ping me''; cl.push({ game: "angry bird", popularity: 67 }); console.log(cl); console.log("clone using ellipses"); var cl3 = [...game_popularity]; cl3.push({ game: "blue whale", popularity: 67 }); cl3[2].game = "harry potter"; console.log(cl3); console.log("clone using json.parse"); var cl4 = JSON.parse(JSON.stringify(game_popularity)); cl4.push({ game: "home alone", popularity: 87 }); cl4[3].game ="lockhead martin"; console.log(cl4); console.log("clone using Object.create"); var cl5 = Array.from(Object.create(game_popularity)); cl5.push({ game: "fish ville", popularity: 87 }); cl5[3].game ="veto power"; console.log(cl5); //array function console.log("sorted original array after clonning"); game_popularity.sort((a, b) => a.popularity < b.popularity); console.log(game_popularity); console.log("Object.assign deep clone object array"); console.log("json.parse deep clone object array"); console.log("concat does not deep clone object array"); console.log("ellipses does not deep clone object array"); console.log("Object.create does not deep clone object array"); Output: sorted original array before clonning [ { game: ''temple runner'', popularity: 86 }, { game: ''fruit ninja'', popularity: 78 }, { game: ''ludo'', popularity: 75 }, { game: ''maze runner'', popularity: 40 }, { game: ''road runner'', popularity: 20 } ] clone using object assign [ { game: ''temple runner'', popularity: 86 }, { game: ''clash of titan'', popularity: 78 }, { game: ''ludo'', popularity: 75 }, { game: ''maze runner'', popularity: 40 }, { game: ''road runner'', popularity: 20 }, { game: ''logan'', popularity: 57 } ] clone using concat [ { game: ''ping me'', popularity: 86 }, { game: ''fruit ninja'', popularity: 78 }, { game: ''ludo'', popularity: 75 }, { game: ''maze runner'', popularity: 40 }, { game: ''road runner'', popularity: 20 }, { game: ''angry bird'', popularity: 67 } ] clone using ellipses [ { game: ''ping me'', popularity: 86 }, { game: ''fruit ninja'', popularity: 78 }, { game: ''harry potter'', popularity: 75 }, { game: ''maze runner'', popularity: 40 }, { game: ''road runner'', popularity: 20 }, { game: ''blue whale'', popularity: 67 } ] clone using json.parse [ { game: ''ping me'', popularity: 86 }, { game: ''fruit ninja'', popularity: 78 }, { game: ''harry potter'', popularity: 75 }, { game: ''lockhead martin'', popularity: 40 }, { game: ''road runner'', popularity: 20 }, { game: ''home alone'', popularity: 87 } ] clone using Object.create [ { game: ''ping me'', popularity: 86 }, { game: ''fruit ninja'', popularity: 78 }, { game: ''harry potter'', popularity: 75 }, { game: ''veto power'', popularity: 40 }, { game: ''road runner'', popularity: 20 }, { game: ''fish ville'', popularity: 87 } ] sorted original array after clonning [ { game: ''ping me'', popularity: 86 }, { game: ''fruit ninja'', popularity: 78 }, { game: ''harry potter'', popularity: 75 }, { game: ''veto power'', popularity: 40 }, { game: ''road runner'', popularity: 20 } ] Object.assign deep clone object array json.parse deep clone object array concat does not deep clone object array ellipses does not deep clone object array Object.create does not deep clone object array


$.evalJSON($.toJSON(origArray));


function deepCloneArray(array) { return Array.from(Object.create(array)); }