Clonar / copiar una instancia de Map

88

¿Cómo clono / copio un mapa en JavaScript?

Sé cómo clonar una matriz, pero ¿cómo clono / copio un mapa?

var myArray = new Array(1, 2, 3);
var copy    = myArray.slice();
// now I can change myArray[0] = 5; & it wont affect copy array

// Can I just do the same for map?
var myMap = new ?? // in javascript is it called a map?
var myMap = {"1": 1, "2", 2};
var copy  = myMap.slice(); 
sazr
fuente
2
ES6 te permitelet copy = {...myMap};
Reactgular

Respuestas:

17

Una forma sencilla (de hacer una copia superficial) es copiar cada propiedad del mapa de origen en el mapa de destino:

var newMap = {};
for (var i in myMap)
   newMap[i] = myMap[i];

NOTA: newMap [i] podría ser una referencia al mismo objeto que myMap [i]

robar
fuente
6
esta es solo una copia superficial ... ¿y si myMap [i] es un mapa en sí mismo?
Stefano
1
Stefano, puedes hacer eso si quieres (verifica si es un objeto con typeof, luego realiza una copia de sus propiedades ... posiblemente recurriendo a la misma función), pero ten en cuenta que ahora debes preocuparte por el posibilidad de que sea un elemento ancestro en su lo que te pondría en un bucle infinito. Si realmente desea una copia profunda, es posible que desee buscar en las bibliotecas para hacer eso.
robo el
4
Lo sé, pero creo que deberías haber escrito esto en tu respuesta en primer lugar ;-)
Stefano
5
Esto no es un mapa sino un objeto. Diferencia pequeña y suble. cf. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
helt
1
No copiará cada propiedad a la que no tendrá acceso a los establecedores y captadores, ya que es solo un objeto
Amante Ninja
329

Con la introducción de Maps en JavaScript es bastante simple considerando que el constructor acepta un iterable:

var newMap = new Map(existingMap)

Documentación aquí: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

tswaters
fuente
4
Una pequeña advertencia a lo anterior: clonar un mapa como este invocará Map.prototype.entriesy Map.prototype.set. Eso significa: si escribe una clase que extiende Map y sobrescribe cualquiera de estos dos métodos, entonces la simple escritura new ExtendedMap( extendedMapObj )no funcionará si los métodos extendidos se basan en propiedades que no están disponibles para el super.
¿Es un clon profundo o simplemente un clon superficial? Digamos que tengo un objeto anidado como valores
Madeo
pero ¿hace una copia profunda o superficial?
Yonatan Nir
5
Esto hará una copia superficial, no profunda: jsfiddle.net/jormwe69
Jaap
1
@PeterCoester ¿Podemos decir que la asintótica de var newMap = new Map(existingMap)es O(n)dónde nestá el número de pares clave / valor del mapa? Supongo que la operación de clonación no es constante O(1)si, como dices, Map.prototype.entries se llama bajo el capó ...
tonix
11

Es muy sencillo clonar un mapa ya que de lo que estás hablando es solo un objeto. Hay un Mapen ES6 que debe buscar, pero para copiar un objeto, simplemente useObject.assign()

let map = {"a": 1, "b": 2}
let copy = Object.assign({}, map);

También puede utilizar cloneDeep()desde Lodash

let copy = cloneDeep(map);
Joshua Michael Waggoner
fuente
6

JQuery tiene un método para extender un objeto (fusionando dos objetos), pero este método también se puede usar para clonar un objeto proporcionando un objeto vacío.

// Shallow copy
var newObject = jQuery.extend({}, oldObject);

// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);

Se puede encontrar más información en la documentación de jQuery .

Pastor Bones
fuente
3

No hay nada integrado.

Utilice una copiadora de propiedades recursiva bien probada o, si el rendimiento no es un problema, serialice en JSON y vuelva a analizar en un nuevo objeto.

alex
fuente
2

No hay clonación / copia incorporada. Puede escribir su propio método en una copia superficial o profunda:

function shallowCopy(obj) {
    var result = {};
    for (var i in obj) {
        result[i] = obj[i];
    }
    return result;
}

function deepCopy(obj) {
    var result = {};
    for (var i in obj) {
        // recursion here, though you'll need some non-trivial logic
        // to avoid getting into an endless loop.
    }
    return result;
}

Todos los objetos en Javascript son dinámicos y se les pueden asignar nuevas propiedades. Un "mapa", como usted lo denomina, es en realidad un objeto vacío. Una matriz también es un objeto, con métodos como slicey propiedades como length.

Nicole
fuente
¡No entendí cuál es la diferencia entre las 2 funciones que escribiste!
Hasan A Yousef
@HasanAYousef La diferencia no está implementada; En una copia profunda, debe recurrir (llamar a deepCopy para cada hijo), pero debido a que los hijos pueden contener una referencia al padre (por ejemplo, window.window2 = window), no puede realizar una copia profunda de esas referencias sin entrar en un bucle sin fin.
Nicole
2

Si necesita hacer una copia profunda de un mapa, puede utilizar lo siguiente:

new Map(JSON.parse(JSON.stringify(Array.from(source))));

Dónde source está el objeto Mapa original?

Tenga en cuenta que esto puede no ser adecuado para todos los casos de uso en los que los valores del mapa no se pueden serializar; para obtener más detalles, consulte: https://stackoverflow.com/a/122704/10583071

robdc
fuente
Ejecuté una prueba en jsperf y descubrí que un enfoque iterativo es 10 veces más rápido: jsperf.com/deep-copy-map
Zack Burt
2
@ZackBurt Lamentablemente, su alternativa propuesta más rápida no crea realmente un deep copyobjetivo Map, es solo un shallow copy. ¿Quizás por eso es tan rápido?
Alfonso M. García Astorga
@ AlfonsoM.GarcíaAstorga Gracias por aclarar (votó a favor en consecuencia). Tiene usted razón en que es no una copia profunda. Pero es una copia más rápida con <10 kb de datos. Lectura complementaria recomendada: v8.dev/blog/cost-of-javascript-2019#json
Zack Burt
1

Noté que Map debería requerir un tratamiento especial, por lo tanto, con todas las sugerencias en este hilo, el código será:

function deepClone( obj ) {
    if( !obj || true == obj ) //this also handles boolean as true and false
        return obj;
    var objType = typeof( obj );
    if( "number" == objType || "string" == objType ) // add your immutables here
        return obj;
    var result = Array.isArray( obj ) ? [] : !obj.constructor ? {} : new obj.constructor();
    if( obj instanceof Map )
        for( var key of obj.keys() )
            result.set( key, deepClone( obj.get( key ) ) );
    for( var key in obj )
        if( obj.hasOwnProperty( key ) )
            result[key] = deepClone( obj[ key ] );
    return result;
}
Dmitriy Pichugin
fuente