145

How do I clone/copy a Map in JavaScript?

I know how to clone an array, but how do I clone/copy a Map?

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(); 
Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
sazr
  • 24,984
  • 66
  • 194
  • 362
  • 6
    ES6 lets you `let copy = {...myMap};` – Reactgular Feb 10 '19 at 14:19
  • 2
    Sorry to be pedantic, but this was _not_ permitted in ES6; rather, it was introduced in ES9 a.k.a ES2018. You could spread arrays in ES6 a.k.a ES2015 but not object literals. – Ray Toal Jul 04 '21 at 07:57

8 Answers8

509

With the introduction of Maps in JavaScript it's quite simple considering the constructor accepts an iterable:

var newMap = new Map(existingMap)

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

nickf
  • 537,072
  • 198
  • 649
  • 721
tswaters
  • 5,343
  • 2
  • 16
  • 8
  • 7
    A small caveat to the above: Cloning a map like this, will invoke `Map.prototype.entries` and `Map.prototype.set`. That means: If you write a class that extends Map _and_ overwrites either of these two methods, then simply writing `new ExtendedMap( extendedMapObj )` will not work if the extended methods rely on properties that are not available to the super. –  Sep 05 '17 at 07:02
  • does it deep clone or just shallow clone? Let's say I have nested object as values – Matteo Aug 28 '19 at 03:23
  • but does it do a deep or a shallow copy?? – CodeMonkey Sep 11 '19 at 08:05
  • 20
    This will do a shallow copy, not deep: https://jsfiddle.net/jormwe69/ – Jaap Oct 21 '19 at 12:33
  • 3
    @PeterCoester Can we say that the asymptotic of `var newMap = new Map(existingMap)` is `O(n)` where `n` is the number of the key/value pairs of the map? I guess that the cloning operation is not constant `O(1)` if, as you say, `Map.prototype.entries` is called under the hood... – tonix Mar 06 '20 at 18:25
  • This should be marked as the true answer. – Zach Dec 23 '20 at 18:12
  • i get an empty {} when i do that in nodeJS – scavenger Jan 19 '21 at 01:31
  • @scavenger it's possible you're giving it a blank iterator? This works as expected: `$ node -p 'new Map([["first", 1], ["second", 2]])'` returns: `Map(2) { 'first' => 1, 'second' => 2 }` – tswaters Mar 28 '22 at 21:40
11

A simple way (to do a shallow copy) is to copy each property of the source map to the target map:

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

NOTE: newMap[i] could very well be a reference to the same object as myMap[i]

vinnyjames
  • 2,040
  • 18
  • 26
rob
  • 9,933
  • 7
  • 42
  • 73
  • 11
    this is a shallow copy only... what if myMap[i] is a map itself? – Stefano Jan 30 '12 at 11:42
  • 1
    Stefano, you can do that if you want (check if if is an object with typeof, then perform a copy of it's properties...possibly by recursing the same function), but keep in mind that now you have to be concerned about the possibility of their being an ancestor element in their which would put you in an infinite loop. If you really want a deep copy, you may want to look into libraries for doing that. – rob Jan 30 '12 at 22:10
  • 4
    I know, but I think you should have written this in your answer in the first place ;-) – Stefano Jan 31 '12 at 10:04
  • 9
    This is not a Map but an Object. Small and suble difference. cf. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map – helt Jul 24 '17 at 10:08
  • 2
    It will not copy each property you wont have the access to setters and getters as its just an object – Amante Ninja Jul 10 '18 at 09:34
  • This does not copy a map into map but into an object. Why is it the preferred answer? – Anton Mitsev Mar 09 '21 at 12:39
9

Very simple to just use Object.assign()

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

// Or

const copy = Object.assign({}, {'a': 1, 'b': 2});
  • 1
    [`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) Warning for Deep Clone: "If the source value is a reference to an object, it only copies the reference value." – Tom Hale Dec 05 '20 at 07:28
6

If you need to make a deep copy of a Map you can use the following:

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

Where source is the original Map object.

Note this may not be suitable for all use cases where the Map values are not serializable, for more details see: https://stackoverflow.com/a/122704/10583071

robdc
  • 69
  • 1
  • 2
  • I ran a test on jsperf and found that an iterative approach is 10x faster: https://jsperf.com/deep-copy-map – Zack Burt Feb 17 '20 at 17:49
  • 3
    @ZackBurt Sadly, your faster proposed alternative does not create really a `deep copy` of the target `Map` it's just a `shallow copy`. Maybe is this why it's so fast? – Alfonso May 28 '20 at 21:39
  • @AlfonsoM.GarcíaAstorga Thank you for clarifying (upvoted accordingly). You are correct in that it is _not_ a deep copy. But it is a faster copy with <10kb of data. Recommended supplemental reading: https://v8.dev/blog/cost-of-javascript-2019#json – Zack Burt May 29 '20 at 12:14
  • this solution is popular throughout the web, but it transforms data and doesn't handle nesting. – ahnbizcad Mar 03 '21 at 19:55
5

JQuery has a method to extend an object (merging two objects), but this method can also be used to clone an object by providing an empty object.

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

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

More information can be found in the jQuery documentation.

Pastor Bones
  • 7,183
  • 3
  • 36
  • 56
3

There is nothing built in.

Either use a well tested recursive property copier or if performance isn't an issue, serialise to JSON and parse again to a new object.

alex
  • 479,566
  • 201
  • 878
  • 984
2

There is no built-in (edit: DEEP) clone/copy. You can write your own method to either shallow or deep copy:

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

[EDIT] Shallow copy is built-in, using Object.assign:

let result = Object.assign({}, obj);

All objects in Javascript are dynamic, and can be assigned new properties. A "map" as you refer to it is actually just an empty object. An Array is also an object, with methods such as slice and properties like length.

Nicole
  • 32,841
  • 11
  • 75
  • 101
  • Did not understand what is the different between the 2 functions you wrote! – Hasan A Yousef Jan 27 '16 at 12:33
  • @HasanAYousef The difference is not implemented; In a deep copy, you must recurse (call deepCopy for each child), but because children may contain a reference to the parent (e.g. window.window2 = window), you cannot deep copy those references without getting into an endless loop. – Nicole Jan 28 '16 at 02:16
  • javascript doesn't even have a copy by value? smh. and people love this language. – ahnbizcad Mar 03 '21 at 19:56
  • @ahnbizcad It's basically exactly the same as Java. tl;dr *everything* is "copy by value" and all values are references except primitives. What it doesn't have natively is a *deep* copy, which neither does Java. Neither does C, I imagine? Most OOP languages don't automatically deep copy complex objects, that would be a nightmare. I recommend learning JS to learn its benefits – Nicole Mar 09 '21 at 19:26
  • deep/shallow copy is a terrible, obscuring term. it's copy by reference. the opposite is copy by value, which primitives are. the language doesn't have copy by value. or even an option to. JSON.stringify doesn't work. it fails at doing multidimensional arrays, and transforms all kinds of data into other data. – ahnbizcad Mar 09 '21 at 21:32
  • @ahnbizcad I think you may be using it wrong. – Nicole Mar 11 '21 at 15:59
1

I noticed that Map should require special treatment, thus with all suggestions in this thread, code will be:

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