Sklonuj / skopiuj instancję mapy

88

Jak sklonować / skopiować mapę w JavaScript?

Wiem, jak sklonować tablicę, ale jak sklonować / skopiować 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(); 
sazr
źródło
2
ES6 pozwalalet copy = {...myMap};
Reactgular

Odpowiedzi:

17

Prostym sposobem (wykonanie płytkiej kopii) jest skopiowanie każdej właściwości mapy źródłowej do mapy docelowej:

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

UWAGA: nowaMapa [i] może równie dobrze być odniesieniem do tego samego obiektu co mojaMapa [i]

obrabować
źródło
6
to jest tylko płytka kopia… co jeśli myMap [i] jest samą mapą?
Stefano
1
Stefano, możesz to zrobić, jeśli chcesz (sprawdź, czy jest to obiekt typu typeof, a następnie wykonaj kopię jego właściwości ... prawdopodobnie powtarzając tę ​​samą funkcję), ale pamiętaj, że teraz musisz się martwić o możliwość bycia ich elementem przodkiem, który wprowadziłby cię w nieskończoną pętlę. Jeśli naprawdę potrzebujesz głębokiej kopii, możesz zajrzeć do bibliotek, aby to zrobić.
obrabować
4
Wiem, ale myślę, że powinieneś był napisać to w swojej odpowiedzi w pierwszej kolejności ;-)
Stefano
5
To nie jest mapa, ale obiekt. Mała i podrzędna różnica. por. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ...
helt
1
Nie skopiuje każdej właściwości, do której nie będziesz mieć dostępu do seterów i pobierających, jako po prostu obiekt
Amante Ninja
329

Wraz z wprowadzeniem Map w JavaScript jest to dość proste, biorąc pod uwagę, że konstruktor akceptuje iterowalne:

var newMap = new Map(existingMap)

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

tswaters
źródło
4
Małe zastrzeżenie do powyższego: klonowanie takiej mapy spowoduje wywołanie Map.prototype.entriesi Map.prototype.set. To znaczy: jeśli napiszesz klasę, która rozszerza Map i nadpisuje którąkolwiek z tych dwóch metod, to po prostu pisanie new ExtendedMap( extendedMapObj )nie zadziała, jeśli metody rozszerzone opierają się na właściwościach, które nie są dostępne dla super.
czy to głęboki klon, czy po prostu płytki klon? Powiedzmy, że mam zagnieżdżony obiekt jako wartości
Madeo
ale czy robi głęboką czy płytką kopię?
Yonatan Nir
5
To zrobi płytką kopię, a nie głęboką: jsfiddle.net/jormwe69
Jaap
1
@PeterCoester Czy możemy powiedzieć, że asymptotyka var newMap = new Map(existingMap)jest O(n)gdzie njest liczba par klucz / wartość na mapie? Chyba operacja klonowania nie jest stała, O(1)jeśli, jak mówisz, Map.prototype.entries jest wywoływana pod maską ...
tonix
11

Bardzo łatwo sklonować mapę, ponieważ to, o czym mówisz, to tylko obiekt. W MapES6 jest znak , który powinieneś sprawdzić, ale aby skopiować obiekt, po prostu użyjObject.assign()

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

Możesz również skorzystać cloneDeep()z Lodash

let copy = cloneDeep(map);
Joshua Michael Wagoner
źródło
6

JQuery ma metodę rozszerzania obiektu (scalanie dwóch obiektów), ale ta metoda może być również używana do klonowania obiektu przez dostarczenie pustego obiektu.

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

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

Więcej informacji można znaleźć w dokumentacji jQuery .

Pastor Bones
źródło
3

Nie ma nic wbudowanego.

Użyj dobrze przetestowanej rekurencyjnej kopiarki właściwości lub jeśli wydajność nie jest problemem, serializuj do JSON i ponownie przeanalizuj do nowego obiektu.

Alex
źródło
2

Nie ma wbudowanej funkcji klonowania / kopiowania. Możesz napisać własną metodę do płytkiej lub głębokiej kopii:

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

Wszystkie obiekty w Javascript są dynamiczne i można im przypisać nowe właściwości. „Mapa”, jak się do niej odnosisz, jest w rzeczywistości po prostu pustym obiektem. Array to także obiekt z metodami takimi jak slicei właściwościami, takimi jak length.

Nicole
źródło
Nie rozumiem, czym się różnią dwie napisane przez Ciebie funkcje!
Hasan A Yousef
@HasanAYousef Różnica nie została zaimplementowana; W głębokiej kopii musisz powtórzyć (wywołaj deepCopy dla każdego dziecka), ale ponieważ dzieci mogą zawierać odniesienie do rodzica (np. Window.window2 = okno), nie możesz skopiować tych odwołań bez wchodzenia w nieskończoną pętlę.
Nicole
2

Jeśli chcesz zrobić dokładną kopię mapy, możesz skorzystać z:

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

Gdzie sourcejest oryginalny obiekt mapy.

Należy pamiętać, że może to nie być odpowiednie dla wszystkich przypadków użycia, w których wartości map nie są serializowane. Aby uzyskać więcej informacji, zobacz: https://stackoverflow.com/a/122704/10583071

robdc
źródło
Przeprowadziłem test na jsperf i stwierdziłem, że iteracyjne podejście jest 10x szybsze: jsperf.com/deep-copy-map
Zack Burt
2
@ZackBurt Niestety, twoja szybsza proponowana alternatywa nie tworzy tak naprawdę deep copycelu Map, a po prostu shallow copy. Może dlatego jest tak szybki?
Alfonso M. García Astorga
@ AlfonsoM.GarcíaAstorga Dziękuję za wyjaśnienie (odpowiednio głosowano). Masz rację, ponieważ nie jest to głęboka kopia. Ale jest to szybsza kopia zawierająca <10kb danych. Zalecana dodatkowa lektura: v8.dev/blog/cost-of-javascript-2019#json
Zack Burt
1

Zauważyłem, że Mapa powinna wymagać specjalnego traktowania, dlatego przy wszystkich sugestiach w tym wątku kod będzie wyglądał następująco:

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
źródło