Reduxプロジェクトのディープコピーマップメソッドを作成しようとしていますが、これは配列ではなくオブジェクトで動作します。 Reduxでは、以前の状態では各状態が何も変わらないはずだと読みました。
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, {...object[key]});
return output;
}, {});
}
できます:
return mapCopy(state, e => {
if (e.id === action.id) {
e.title = 'new item';
}
return e;
})
ただし、内部アイテムはディープコピーされないため、次のように微調整する必要があります。
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;
}, {});
}
どのオブジェクトが渡されるかを知る必要があるため、これはエレガントではありません。 ES6でスプレッド構文を使用してオブジェクトをディープコピーする方法はありますか?
そのような機能はES6に組み込まれていません。やりたいことに応じて、いくつかのオプションがあると思います。
本当にディープコピーしたい場合:
cloneDeep
メソッドがあります。ただし、いくつかの点を変更する場合は、作業を節約できます。私はあなたがあなたの機能へのすべての呼び出しサイトを制御すると仮定しています。
mapCopy
に渡されるすべてのコールバックは、既存のオブジェクトを変更するのではなく、新しいオブジェクトを返す必要があることを指定します。例えば:
mapCopy(state, e => {
if (e.id === action.id) {
return Object.assign({}, e, {
title: 'new item'
});
} else {
return e;
}
});
これは Object.assign
を使用して新しいオブジェクトを作成し、その新しいオブジェクトにe
のプロパティを設定してから、その新しいオブジェクトに新しいタイトルを設定します。つまり、既存のオブジェクトを変更することはなく、必要な場合にのみ新しいオブジェクトを作成します。
mapCopy
が本当に簡単になりました:
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, object[key]);
return output;
}, {});
}
基本的に、mapCopy
は呼び出し元が正しいことをすることを信頼しています。これが、あなたがすべての通話サイトを管理していると仮定した理由です。
代わりにこれをディープコピーに使用します
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);
MDNから
注:Spread構文は、配列のコピー中に事実上1レベル深くなります。したがって、次の例が示すように、多次元配列のコピーには適さない可能性があります(Object.assign()およびspread構文でも同じです)。
個人的には、マルチレベルのオブジェクト/配列のクローン作成に LodashのcloneDeep 関数を使用することをお勧めします。
これが実際の例です:
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>
私はこれを頻繁に使用します:
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;
}, {})
}
}
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;
}
}
// 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
}
JavaScriptでオブジェクトを機能的にディープコピーする方法を探していました。見つからなかったので作りました。このアルゴリズムは不変であり、副作用は発生せず、命令型ではなく宣言型です。楽しい!
const deepCopy = obj => (Array.isArray(obj) ? Object.values : obj=>obj)(Object.keys(obj).reduce((acc, key) =>
({...acc, [key]: (
!obj[key] ? obj[key]
: typeof obj[key] === 'object' ? deepCopy(obj[key])
: obj[key]
)}),
{}
))
基本的には、オブジェクトのキーが別のオブジェクトに削減されています。ネストされたオブジェクトと配列を再帰的に処理し、配列をコピーする前にキーとして使用される数値インデックスを持つオブジェクトに変換します。ネストされた配列をオブジェクトに型変換して戻すには、Object.valuesを使用します。
上記の関数は、次の一連の操作を実行します。