2018.11.05 若干変更して別記事にまとめました。
#2018.10.24追記
これでいいみたい。ついでにSetも足してみました。
const mapForMap = f => a => new Map( [...a].map( f ) );
const mapForSet = f => a => new Set( [...a].map( f ) );
const mapFromEntriesIntoObj = f => xs =>
xs.reduce((acc, e)=>( { ...acc, [e[0]]:f(e[1]) } ), {} );
const mapForObj = f => a =>
mapFromEntriesIntoObj( f )( Object.entries( a ) );
const getType = a => Object.prototype.toString.call(a).slice(8,-1) ;
const clone = a => {
const type = getType( a );
return (type === "Array")? a.map( clone )
:(type === "Map")? mapForMap( clone )( a )
:(type === "Set")? mapForSet( clone )( a )
:(type === "Object")? mapForObj( clone )( a )
: a;
}
#以下、過去記事
完璧に何にでも対応できるディープコピーはやっぱり無理なようで(あったら教えてください)。
で、自分が困らない程度に作ってみました。
- Map,Object用のmap関数を作る。
- 引数が何なのか、文字列で取得。
- 引数がArray,Map,Objectなら要素に再帰的にcloneを適用
- それ以外ならそのまま返す
const clone = a => {
const mapForMap = f => a => {
const b = new Map();
[...a.keys()].forEach( e => b.set( e, f(a.get(e)) ) );
return b;
}
const mapForObj = f => a => {
const b = {};
Object.keys(a).forEach( e => b[e] = f(a[e]) );
return b;
}
const type = Object.prototype.toString.call(a).slice(8,-1) ;
if(type === "Array") return a.map( clone );
if(type === "Map") return mapForMap( clone )( a );
if(type === "Object") return mapForObj( clone )( a );
return a;
}
class myClass{
constructor(name){
this.name=name
}
}
const object = {
a: 1,
b: 'a',
c: '',
d: null,
e: undefined,
f: true,
g: [1, 2, 3],
h: function () { console.log('h'); },
i: {
a: 1,
b: 'a',
c: '',
d: null,
e: undefined,
f: true,
g: [1, 2, 3],
h: function () { console.log('h'); },
i: { a: 1 },
j: new Map([ [ 0, 0 ], [ 1, 1 ] ]) ,
k: new myClass("Q")
},
j: new Map([ [ 0, 0 ], [ 1, 1 ] ]) ,
k: new myClass("Q")
}
const cloned = clone(object)
console.log(cloned)
/* =>
{
a: 1,
b: 'a',
c: '',
d: null,
e: undefined,
f: true,
g: [ 1, 2, 3 ],
h: [Function: h],
i:
{ a: 1,
b: 'a',
c: '',
d: null,
e: undefined,
f: true,
g: [ 1, 2, 3 ],
h: [Function: h],
i: { a: 1 },
j: Map { 0 => 0, 1 => 1 },
k: { name: 'Q' } },
j: Map { 0 => 0, 1 => 1 },
k: { name: 'Q' }
}
*/
だいたいは思ったとおりです。使えそう。
自前で定義したクラスmyClassはObjectに変わってしまっています。
Mapを特定したいので.toStringメソッドを使っていますが、これもやろうと思えば簡単に上書きできるので、そういうことがありうる環境では使えないかもです。
参考記事:【JavaScript】オブジェクトをDeepCopyするclone関数を書いた
ES6のObject.assignがシャローコピーなのでディープコピーする方法を考える