Próbuję utworzyć metodę głębokiej mapy dla mojego projektu Redux, która będzie działać z obiektami, a nie z tablicami. Czytałem, że w Redux każdy stan nie powinien nic zmieniać w poprzednich stanach.
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, {...object[key]});
return output;
}, {});
}
To działa:
return mapCopy(state, e => {
if (e.id === action.id) {
e.title = 'new item';
}
return e;
})
Jednak nie kopiuje głęboko elementów wewnętrznych, więc muszę go dostosować, aby:
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;
}, {});
}
Jest to mniej eleganckie, ponieważ wymaga wiedzy, które obiekty są przekazywane. Czy w ES6 jest sposób, aby użyć składni rozkładówek do głębokiego kopiowania obiektu?
javascript
ecmascript-6
redux
spread-syntax
Chłopak
źródło
źródło
combineReducers
do skomponowania dwóch (lub więcej) razem. Jeśli używasz idiomatycznych technik redux, problem głębokiego klonowania obiektów znika.Odpowiedzi:
Żadna taka funkcjonalność nie jest wbudowana w ES6. Myślę, że masz kilka opcji w zależności od tego, co chcesz zrobić.
Jeśli naprawdę chcesz głęboko skopiować:
cloneDeep
metodę.Alternatywne rozwiązanie konkretnego problemu (bez głębokiej kopii)
Jednak myślę, że jeśli chcesz zmienić kilka rzeczy, możesz zaoszczędzić sobie trochę pracy. Zakładam, że kontrolujesz wszystkie strony wywołujące swoją funkcję.
Określ, że wszystkie wywołania zwrotne przekazane do
mapCopy
muszą zwracać nowe obiekty zamiast mutowania istniejącego obiektu. Na przykład:mapCopy(state, e => { if (e.id === action.id) { return Object.assign({}, e, { title: 'new item' }); } else { return e; } });
Służy
Object.assign
do tworzenia nowego obiektu, ustawia właściwoście
tego nowego obiektu, a następnie ustawia nowy tytuł dla tego nowego obiektu. Oznacza to, że nigdy nie modyfikujesz istniejących obiektów i tworzysz nowe tylko wtedy, gdy jest to konieczne.mapCopy
może być teraz naprawdę proste:export const mapCopy = (object, callback) => { return Object.keys(object).reduce(function (output, key) { output[key] = callback.call(this, object[key]); return output; }, {}); }
Zasadniczo
mapCopy
polega na zaufaniu rozmówcom, że postąpią właściwie. Dlatego właśnie powiedziałem, że przy założeniu, że kontrolujesz wszystkie strony połączeń.źródło
Zamiast tego użyj tego do głębokiej kopii
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);
źródło
Z MDN
Osobiście sugeruję użycie funkcji cloneDeep firmy Lodash do wielopoziomowego klonowania obiektów / tablic.
Oto działający przykład:
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>
źródło
Run code snippet
i powinien działać poprawnie.Często używam tego:
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; }, {}) } }
źródło
const a = { foods: { dinner: 'Pasta' } } let b = JSON.parse(JSON.stringify(a)) b.foods.dinner = 'Soup' console.log(b.foods.dinner) // Soup console.log(a.foods.dinner) // Pasta
Korzystanie
JSON.stringify
iJSON.parse
jest najlepszym sposobem. Ponieważ używając operatora spread, nie uzyskamy skutecznej odpowiedzi, gdy obiekt json zawiera w sobie inny obiekt. musimy to ręcznie określić.źródło
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; } }
źródło
// 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 }
źródło
const cloneData = (dataArray) => { newData= [] dataArray.forEach((value) => { newData.push({...value}) }) return newData }
źródło
Sam wylądowałem na tych odpowiedziach zeszłego dnia, próbując znaleźć sposób na głębokie kopiowanie złożonych struktur, które mogą obejmować linki rekurencyjne. Ponieważ nie byłem zadowolony z niczego sugerowanego wcześniej, sam zaimplementowałem to koło. I działa całkiem dobrze. Mam nadzieję, że to komuś pomoże.
Przykładowe użycie:
OriginalStruct.deep_copy = deep_copy; // attach the function as a method TheClone = OriginalStruct.deep_copy();
Proszę spojrzeć na https://github.com/latitov/JS_DeepCopy, aby zobaczyć przykłady na żywo, jak go używać, a także jest tam deep_print ().
Jeśli potrzebujesz tego szybko, oto źródło funkcji deep_copy ():
function deep_copy() { 'use strict'; // required for undef test of 'this' below // Copyright (c) 2019, Leonid Titov, Mentions Highly Appreciated. var id_cnt = 1; var all_old_objects = {}; var all_new_objects = {}; var root_obj = this; if (root_obj === undefined) { console.log(`deep_copy() error: wrong call context`); return; } var new_obj = copy_obj(root_obj); for (var id in all_old_objects) { delete all_old_objects[id].__temp_id; } return new_obj; // function copy_obj(o) { var new_obj = {}; if (o.__temp_id === undefined) { o.__temp_id = id_cnt; all_old_objects[id_cnt] = o; all_new_objects[id_cnt] = new_obj; id_cnt ++; for (var prop in o) { if (o[prop] instanceof Array) { new_obj[prop] = copy_array(o[prop]); } else if (o[prop] instanceof Object) { new_obj[prop] = copy_obj(o[prop]); } else if (prop === '__temp_id') { continue; } else { new_obj[prop] = o[prop]; } } } else { new_obj = all_new_objects[o.__temp_id]; } return new_obj; } function copy_array(a) { var new_array = []; if (a.__temp_id === undefined) { a.__temp_id = id_cnt; all_old_objects[id_cnt] = a; all_new_objects[id_cnt] = new_array; id_cnt ++; a.forEach((v,i) => { if (v instanceof Array) { new_array[i] = copy_array(v); } else if (v instanceof Object) { new_array[i] = copy_object(v); } else { new_array[i] = v; } }); } else { new_array = all_new_objects[a.__temp_id]; } return new_array; } }
Twoje zdrowie@!
źródło
Oto funkcja deepClone, która obsługuje wszystkie typy danych pierwotnych, tablic, obiektów i funkcji
function deepClone(obj){ if(Array.isArray(obj)){ var arr = []; for (var i = 0; i < obj.length; i++) { arr[i] = deepClone(obj[i]); } return arr; } if(typeof(obj) == "object"){ var cloned = {}; for(let key in obj){ cloned[key] = deepClone(obj[key]) } return cloned; } return obj; } console.log( deepClone(1) ) console.log( deepClone('abc') ) console.log( deepClone([1,2]) ) console.log( deepClone({a: 'abc', b: 'def'}) ) console.log( deepClone({ a: 'a', num: 123, func: function(){'hello'}, arr: [[1,2,3,[4,5]], 'def'], obj: { one: { two: { three: 3 } } } }) )
źródło
Oto mój algorytm głębokiego kopiowania.
const DeepClone = (obj) => { if(obj===null||typeof(obj)!=='object')return null; let newObj = { ...obj }; for (let prop in obj) { if ( typeof obj[prop] === "object" || typeof obj[prop] === "function" ) { newObj[prop] = DeepClone(obj[prop]); } } return newObj; };
źródło