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(); 

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]


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


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

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 .


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.


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.


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


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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.