link822 link823 link824 link825 link826 link827 link828 link829 link830 link831 link832 link833 link834 link835 link836 link837 link838 link839 link840 link841 link842 link843 link844 link845 link846 link847 link848 link849 link850 link851 link852 link853 link854 link855 link856 link857 link858 link859 link860 link861 link862 link863 link864 link865 link866 link867 link868 link869 link870 link871 link872 link873 link874 link875 link876 link877 link878 link879 link880 link881 link882 link883 link884 link885 link886 link887 link888 link889 link890 link891 link892 link893 link894 link895 link896 link897 link898 link899 link900 link901 link902 link903 link904 link905 link906 link907 link908 link909 link910 link911 link912 link913 link914 link915 link916 link917 link918 link919 link920 link921 link922 link923 link924 link925 link926 link927 link928 link929 link930 link931 link932 link933 link934 link935 link936 link937 link938 link939 link940 link941 link942 link943 link944 link945 link946 link947 link948 link949 link950 link951 link952 link953 link954 link955 link956 link957 link958

[Vue.js] Deep copy in ES6 using the spread syntax Subscribe to RSS

when trying to create a deep copy map method for my Redux project that will work with objects rather than arrays. I read that in Redux each state should not change anything in the previous states.

export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {

output[key] = callback.call(this, {…object[key]});

return output;
}, {});
}

It works:

return mapCopy(state, e => {

if (e.id === action.id) {
e.title = ‘new item’;
}

return e;
})

However it does not deep copy inner items so I need to tweak it to:

export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {

let newObject = {…object[key]};
newObject.style = {…newObject.style};
newObject.data = {…newObject.data};

output[key] = callback.call(this, newObject);

return output;
}, {});
}

This is less elegant as it requires to know which objects are passed.
Is there a way in ES6 to use the spread syntax to deep copy an object?

Solution :

No such functionality is built-in to ES6. I think you have a couple of options depending on what you want to do.

If you really want to deep copy:

Use a library. For example, lodash has a cloneDeep method.
Implement the own cloning function.

Alternative Solution To the Specific Problem (No Deep Copy)

However, I think, if you’re willing to change a couple things, you can save yourself some work. I’m assuming you control all call sites to the function.

Specify that all callbacks passed to mapCopy must return new objects instead of mutating the existing object. For example:

mapCopy(state, e => {
if (e.id === action.id) {
return Object.assign({}, e, {
title: ‘new item’
});
} else {
return e;
}
});

This makes use of Object.assign to create a new object, sets properties of e on that new object, then sets a new title on that new object. This means you never mutate existing objects and only create new ones when necessary.
mapCopy can be really simple now:

export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, object[key]);
return output;
}, {});
}

Essentially, mapCopy is trusting its callers to do the right thing. This is why I said this assumes you control all call sites.

Solution 2:

Instead use this for deep copy

var newObject = JSON.parse(JSON.stringify(oldObject))

var oldObject = {
name: ‘A’,
address: {
street: ‘Station Road’,
city: ‘Pune’
}
}
var newObject = JSON.parse(JSON.stringify(oldObject));

newObject.address.city = ‘Delhi’;
console.log(‘newObject’);
console.log(newObject);
console.log(‘oldObject’);
console.log(oldObject);

Solution 3:

From MDN

Note: Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays as the following example shows (it’s the same with Object.assign() and spread syntax).

Personally, I suggest using Lodash’s cloneDeep function for multi-level object/array cloning.

Here is a working example:

const arr1 = [{ ‘a’: 1 }];

const arr2 = […arr1];

const arr3 = _.clone(arr1);

const arr4 = arr1.slice();

const arr5 = _.cloneDeep(arr1);

const arr6 = […{…arr1}]; // a bit ugly syntax but it is working!

// first level
console.log(arr1 === arr2); // false
console.log(arr1 === arr3); // false
console.log(arr1 === arr4); // false
console.log(arr1 === arr5); // false
console.log(arr1 === arr6); // false

// second level
console.log(arr1[0] === arr2[0]); // true
console.log(arr1[0] === arr3[0]); // true
console.log(arr1[0] === arr4[0]); // true
console.log(arr1[0] === arr5[0]); // false
console.log(arr1[0] === arr6[0]); // false
<script src=”https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

Solution 4:

I often use this:

function deepCopy(obj) {
if(typeof obj !== ‘object’ || obj === null) {
return obj;
}

if(obj instanceof Date) {
return new Date(obj.getTime());
}

if(obj instanceof Array) {
return obj.reduce((arr, item, i) => {
arr[i] = deepCopy(item);
return arr;
}, []);
}

if(obj instanceof Object) {
return Object.keys(obj).reduce((newObj, key) => {
newObj[key] = deepCopy(obj[key]);
return newObj;
}, {})
}
}

Solution 5:

function deepclone(obj) {
let newObj = {};

if (typeof obj === ‘object’) {
for (let key in obj) {
let property = obj[key],
type = typeof property;
switch (type) {
case ‘object’:
if( Object.prototype.toString.call( property ) === ‘[object Array]‘ ) {
newObj[key] = [];
for (let item of property) {
newObj[key].push(this.deepclone(item))
}
} else {
newObj[key] = deepclone(property);
}
break;
default:
newObj[key] = property;
break;

}
}
return newObj
} else {
return obj;
}
}

Solution 6:

// use: clone( <thing to copy> ) returns <new copy>
// untested use at own risk
function clone(o, m){
// return non object values
if(‘object’ !==typeof o) return o
// m: a map of old refs to new object refs to stop recursion
if(‘object’ !==typeof m || null ===m) m =new WeakMap()
var n =m.get(o)
if(‘undefined’ !==typeof n) return n
// shallow/leaf clone object
var c =Object.getPrototypeOf(o).constructor
// TODO: specialize copies for expected built in types i.e. Date etc
switch(c) {
// shouldn’t be copied, keep reference
case Boolean:
case Error:
case Function:
case Number:
case Promise:
case String:
case Symbol:
case WeakMap:
case WeakSet:
n =o
break;
// array like/collection objects
case Array:
m.set(o, n =o.slice(0))
// recursive copy for child objects
n.forEach(function(v,i){
if(‘object’ ===typeof v) n[i] =clone(v, m)
});
break;
case ArrayBuffer:
m.set(o, n =o.slice(0))
break;
case DataView:
m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.byteLength))
break;
case Map:
case Set:
m.set(o, n =new (c)(clone(Array.from(o.entries()), m)))
break;
case Int8Array:
case Uint8Array:
case Uint8ClampedArray:
case Int16Array:
case Uint16Array:
case Int32Array:
case Uint32Array:
case Float32Array:
case Float64Array:
m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.length))
break;
// use built in copy constructor
case Date:
case RegExp:
m.set(o, n =new (c)(o))
break;
// fallback generic object copy
default:
m.set(o, n =Object.assign(new (c)(), o))
// recursive copy for child objects
for(c in n) if(‘object’ ===typeof n[c]) n[c] =clone(n[c], m)
}
return n
}