Cloner / copier une instance de Map

88

Comment cloner / copier une carte en JavaScript?

Je sais comment cloner un tableau mais comment cloner / copier une carte?

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
la source
2
ES6 vous permetlet copy = {...myMap};
Reactgular

Réponses:

17

Un moyen simple (pour faire une copie superficielle) est de copier chaque propriété de la carte source sur la carte cible:

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

REMARQUE: newMap [i] pourrait très bien être une référence au même objet que myMap [i]

Rob
la source
6
ceci est une copie superficielle seulement ... et si myMap [i] est une carte elle-même?
Stefano
1
Stefano, vous pouvez le faire si vous le souhaitez (vérifiez si si est un objet avec typeof, puis effectuez une copie de ses propriétés ... éventuellement en répétant la même fonction), mais gardez à l'esprit que maintenant vous devez vous préoccuper du possibilité qu'ils soient un élément ancêtre dans leur qui vous mettrait dans une boucle infinie. Si vous voulez vraiment une copie complète, vous voudrez peut-être consulter les bibliothèques pour le faire.
rob
4
Je sais, mais je pense que vous auriez dû écrire ceci dans votre réponse en premier lieu ;-)
Stefano
5
Ce n'est pas une carte mais un objet. Petite et suble différence. cf. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Helt
1
Il ne copiera pas chaque propriété que vous n'aurez pas accès aux setters et aux getters car c'est juste un objet
Amante Ninja
329

Avec l'introduction de Maps en JavaScript, c'est assez simple étant donné que le constructeur accepte un itérable:

var newMap = new Map(existingMap)

Documentation ici: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

tswaters
la source
4
Une petite mise en garde à ce qui précède: le clonage d'une carte comme celle-ci invoquera Map.prototype.entrieset Map.prototype.set. Cela signifie: si vous écrivez une classe qui étend Map et écrase l'une ou l'autre de ces deux méthodes, la simple écriture new ExtendedMap( extendedMapObj )ne fonctionnera pas si les méthodes étendues reposent sur des propriétés qui ne sont pas disponibles pour le super.
est-ce un clone profond ou juste un clone peu profond? Disons que j'ai un objet imbriqué en tant que valeurs
Madeo
mais fait-il une copie profonde ou superficielle ??
Yonatan Nir le
5
Cela fera une copie superficielle, pas profonde: jsfiddle.net/jormwe69
Jaap
1
@PeterCoester Peut-on dire que l'asymptotique de var newMap = new Map(existingMap)est O(n)nest le nombre de paires clé / valeur de la carte? Je suppose que l'opération de clonage n'est pas constante O(1)si, comme vous le dites, Map.prototype.entries est appelé sous le capot ...
tonix
11

Très simple pour cloner une carte puisque ce dont vous parlez n'est qu'un objet. Il y a un Mapdans ES6 que vous devriez rechercher, mais pour copier un objet, utilisez simplementObject.assign()

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

Vous pouvez également utiliser cloneDeep()depuis Lodash

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

JQuery a une méthode pour étendre un objet (fusion de deux objets), mais cette méthode peut également être utilisée pour cloner un objet en fournissant un objet vide.

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

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

Plus d'informations peuvent être trouvées dans la documentation jQuery .

Pasteur Bones
la source
3

Il n'y a rien de intégré.

Utilisez un copieur de propriétés récursives bien testé ou, si les performances ne sont pas un problème, sérialisez vers JSON et analysez à nouveau vers un nouvel objet.

Alex
la source
2

Il n'y a pas de clone / copie intégré. Vous pouvez écrire votre propre méthode en copie superficielle ou profonde:

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;
}

Tous les objets en Javascript sont dynamiques et peuvent se voir attribuer de nouvelles propriétés. Une "carte" comme vous y parlez n'est en fait qu'un objet vide. Un Array est également un objet, avec des méthodes telles que sliceet des propriétés telles que length.

Nicole
la source
Je n'ai pas compris quelle est la différence entre les 2 fonctions que vous avez écrites!
Hasan A Yousef
@HasanAYousef La différence n'est pas implémentée; Dans une copie profonde, vous devez recurse (appelez deepCopy pour chaque enfant), mais comme les enfants peuvent contenir une référence au parent (par exemple window.window2 = window), vous ne pouvez pas copier en profondeur ces références sans entrer dans une boucle sans fin.
Nicole
2

Si vous avez besoin de faire une copie complète d'une carte, vous pouvez utiliser les éléments suivants:

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

Où se sourcetrouve l'objet Map d'origine.

Notez que cela peut ne pas convenir à tous les cas d'utilisation où les valeurs de la carte ne sont pas sérialisables, pour plus de détails, voir: https://stackoverflow.com/a/122704/10583071

robdc
la source
J'ai effectué un test sur jsperf et j'ai trouvé qu'une approche itérative est 10 fois plus rapide: jsperf.com/deep-copy-map
Zack Burt le
2
@ZackBurt Malheureusement, l'alternative proposée plus rapidement ne crée pas vraiment une deep copycible, Mapc'est juste un shallow copy. C'est peut-être pourquoi c'est si rapide?
Alfonso M. García Astorga
@ AlfonsoM.GarcíaAstorga Merci d'avoir clarifié (voté en conséquence). Vous avez raison, ce n'est pas une copie complète. Mais c'est une copie plus rapide avec <10 Ko de données. Lecture supplémentaire recommandée: v8.dev/blog/cost-of-javascript-2019#json
Zack Burt
1

J'ai remarqué que Map devrait nécessiter un traitement spécial, donc avec toutes les suggestions de ce fil, le code sera:

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
la source