Clonar / copiar uma instância do mapa

88

Como faço para clonar / copiar um mapa em JavaScript?

Eu sei como clonar uma matriz, mas como faço para clonar / copiar um 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
fonte
2
ES6 permitelet copy = {...myMap};
Reactgular de

Respostas:

17

Uma maneira simples (de fazer uma cópia superficial) é copiar cada propriedade do mapa de origem para o mapa de destino:

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

NOTA: newMap [i] pode muito bem ser uma referência ao mesmo objeto que myMap [i]

roubar
fonte
6
esta é apenas uma cópia superficial ... e se myMap [i] for um mapa em si?
Stefano
1
Stefano, você pode fazer isso se quiser (verifique se é um objeto com typeof, então execute uma cópia de suas propriedades ... possivelmente recorrendo à mesma função), mas tenha em mente que agora você deve se preocupar com o possibilidade de serem um elemento ancestral em seus, o que o colocaria em um loop infinito. Se você realmente deseja uma cópia profunda, pode procurar bibliotecas para fazer isso.
roubar
4
Eu sei, mas acho que você deveria ter escrito isso em sua resposta em primeiro lugar ;-)
Stefano
5
Este não é um mapa, mas um objeto. Diferença pequena e sutil. cf. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
atendido em
1
Ele não copiará cada propriedade que você não terá acesso a setters e getters como se fossem apenas um objeto
Amante Ninja
329

Com a introdução do Maps em JavaScript é bastante simples, considerando que o construtor aceita um iterável:

var newMap = new Map(existingMap)

Documentação aqui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

tswaters
fonte
4
Uma pequena advertência ao acima: clonar um mapa como este invocará Map.prototype.entriese Map.prototype.set. Isso significa: se você escrever uma classe que estende Map e sobrescreve um desses dois métodos, simplesmente escrever new ExtendedMap( extendedMapObj )não funcionará se os métodos estendidos dependerem de propriedades que não estão disponíveis para o super.
ele clona profundamente ou apenas clone raso? Digamos que eu tenha aninhado objetos como valores
Madeo
mas faz uma cópia profunda ou superficial ??
Yonatan Nir
5
Isso fará uma cópia superficial, não profunda: jsfiddle.net/jormwe69
Jaap
1
@PeterCoester Podemos dizer que a assintótica de var newMap = new Map(existingMap)é O(n)onde nestá o número dos pares chave / valor do mapa? Eu acho que a operação de clonagem não é constante O(1)se, como você diz, Map.prototype.entries é chamada sob o capô ...
tonix
11

É muito simples clonar um mapa, pois o que você está falando é apenas um objeto. Existe um Mapno ES6 que você deve procurar, mas para copiar um objeto, basta usarObject.assign()

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

Você também pode usar cloneDeep()do Lodash

let copy = cloneDeep(map);
Joshua Michael Wagoner
fonte
6

JQuery tem um método para estender um objeto (mesclando dois objetos), mas esse método também pode ser usado para clonar um objeto fornecendo um objeto vazio.

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

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

Mais informações podem ser encontradas na documentação do jQuery .

Pastor Bones
fonte
3

Não há nada embutido.

Use uma copiadora de propriedade recursiva bem testada ou, se o desempenho não for um problema, serialize para JSON e analise novamente para um novo objeto.

alex
fonte
2

Não há clone / cópia embutido. Você pode escrever seu próprio método para cópia superficial ou 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 os objetos em Javascript são dinâmicos e podem receber novas propriedades. Um "mapa", como você se refere, é na verdade apenas um objeto vazio. Um Array também é um objeto, com métodos como slicee propriedades como length.

Nicole
fonte
Não entendi qual é a diferença entre as 2 funções que você escreveu!
Hasan A Yousef
@HasanAYousef A diferença não foi implementada; Em uma cópia profunda, você deve recursar (chamar deepCopy para cada filho), mas como os filhos podem conter uma referência ao pai (por exemplo, window.window2 = janela), você não pode copiar profundamente essas referências sem entrar em um loop infinito.
Nicole
2

Se você precisar fazer uma cópia profunda de um mapa, poderá usar o seguinte:

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

Onde source está o objeto Mapa original.

Observe que isso pode não ser adequado para todos os casos de uso em que os valores do mapa não são serializáveis. Para obter mais detalhes, consulte: https://stackoverflow.com/a/122704/10583071

robdc
fonte
Fiz um teste no jsperf e descobri que uma abordagem iterativa é 10 vezes mais rápida: jsperf.com/deep-copy-map
Zack Burt
2
@ZackBurt Infelizmente, sua alternativa mais rápida proposta não cria realmente um deep copydo alvo Map, é apenas um shallow copy. Talvez seja por isso que é tão rápido?
Alfonso M. García Astorga
@ AlfonsoM.GarcíaAstorga Obrigado por esclarecer (votado em conformidade). Você está correto ao dizer que não é uma cópia profunda. Mas é uma cópia mais rápida com <10kb de dados. Leitura complementar recomendada: v8.dev/blog/cost-of-javascript-2019#json
Zack Burt
1

Percebi que o Map deve requerer um tratamento especial, portanto, com todas as sugestões neste thread, o 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
fonte