Клонировать / копировать экземпляр карты

88

Как клонировать / копировать карту в JavaScript?

Я знаю, как клонировать массив, но как клонировать / копировать карту?

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 позволяет вамlet copy = {...myMap};
Reactgular

Ответы:

17

Простой способ (сделать неглубокую копию) - скопировать каждое свойство исходной карты в целевую карту:

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

ПРИМЕЧАНИЕ: newMap [i] вполне может быть ссылкой на тот же объект, что и myMap [i].

грабить
источник
6
это только мелкая копия ... что, если myMap [i] - это сама карта?
Стефано
1
Стефано, вы можете сделать это, если хотите (проверьте, является ли объект с typeof, затем выполните копию его свойств ... возможно, повторяя ту же функцию), но имейте в виду, что теперь вы должны беспокоиться о возможность того, что они являются их предком, что поставит вас в бесконечный цикл. Если вам действительно нужна глубокая копия, вы можете изучить библиотеки для этого.
rob
4
Я знаю, но я думаю, что вы должны были написать это в первую очередь в своем ответе ;-)
Стефано
5
Это не карта, а объект. Небольшая и субарендная разница. ср. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
helt
1
Он не будет копировать каждое свойство, которое у вас не будет доступа к сеттерам и геттерам, поскольку это просто объект
Amante Ninja
329

С введением Maps в JavaScript это довольно просто, учитывая, что конструктор принимает итерацию:

var newMap = new Map(existingMap)

Документация здесь: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

Цуотерс
источник
4
Небольшое замечание по поводу вышеизложенного: при клонировании карты, подобной этой, будет вызываться Map.prototype.entriesи Map.prototype.set. Это означает: если вы напишете класс, который расширяет Map и перезаписывает любой из этих двух методов, то простая запись new ExtendedMap( extendedMapObj )не будет работать, если расширенные методы полагаются на свойства, которые недоступны для super.
это глубокий клон или просто мелкий клон? Допустим, у меня есть вложенный объект в качестве значений
Мадео
но делает ли он глубокую или мелкую копию ??
Йонатан Нир
5
Это будет неглубокая копия, а не глубокая: jsfiddle.net/jormwe69
Jaap
1
@PeterCoester Можем ли мы сказать, что асимптотика var newMap = new Map(existingMap)- это O(n)где nколичество пар ключ / значение карты? Я предполагаю, что операция клонирования непостоянна, O(1)если, как вы говорите, Map.prototype.entries вызывается под капотом ...
tonix
11

Очень просто клонировать карту, поскольку то, о чем вы говорите, - это просто объект. В MapES6 есть пункт, который вы должны найти, но чтобы скопировать объект, просто используйтеObject.assign()

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

Вы также можете использовать cloneDeep()из Lodash

let copy = cloneDeep(map);
Джошуа Майкл Ваггонер
источник
6

В JQuery есть метод расширения объекта (слияние двух объектов), но этот метод также можно использовать для клонирования объекта, предоставив пустой объект.

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

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

Дополнительную информацию можно найти в документации jQuery .

Пастор Боунс
источник
3

Нет ничего встроенного.

Либо используйте хорошо протестированный рекурсивный копировщик свойств, либо, если производительность не является проблемой, сериализуйте его в JSON и снова выполните синтаксический анализ для нового объекта.

Алекс
источник
2

Встроенного клона / копии нет. Вы можете написать свой собственный метод для мелкой или глубокой копии:

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

Все объекты в Javascript являются динамическими, и им можно назначать новые свойства. «Карта», как вы ее называете, на самом деле является просто пустым объектом. Массив также является объектом с такими методами sliceи свойствами, как length.

Николь
источник
Не понял, чем отличаются 2 написанные вами функции!
Hasan A Yousef
@HasanAYousef Разница не реализована; В глубокой копии вы должны выполнять рекурсию (вызывать deepCopy для каждого дочернего элемента), но поскольку дочерние элементы могут содержать ссылку на родительский элемент (например, window.window2 = window), вы не можете глубоко скопировать эти ссылки, не попадая в бесконечный цикл.
Николь
2

Если вам нужно сделать полную копию карты, вы можете использовать следующее:

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

куда source находится исходный объект Map.

Обратите внимание, что это может не подходить для всех случаев использования, когда значения карты не сериализуемы, для получения дополнительной информации см .: https://stackoverflow.com/a/122704/10583071

robdc
источник
Я провел тест на jsperf и обнаружил, что итеративный подход в 10 раз быстрее: jsperf.com/deep-copy-map
Зак Берт
2
@ZackBurt К сожалению, ваша более быстрая предлагаемая альтернатива не создает на самом деле deep copyцелевого объекта, Mapэто просто объект shallow copy. Может поэтому так быстро?
Альфонсо М. Гарсия Асторга
@ AlfonsoM.GarcíaAstorga Спасибо за разъяснения (проголосовали соответственно). Вы правы в том, что это не полная копия. Но это более быстрая копия с объемом данных <10 КБ. Рекомендуемая дополнительная литература: v8.dev/blog/cost-of-javascript-2019#json
Зак Берт,
1

Я заметил, что Map требует особого обращения, поэтому со всеми предложениями в этой ветке код будет:

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;
}
Дмитрий Пичугин
источник