前記事の続きです。
引数の中味をあんまり気にしないでディープコピーできる関数cloneです。
前記事ではこう書きました。
const mapForMap = f => a =>
new Map( [...a].map( ([key, value]) =>[key, f(value)] ) );
const mapForSet = f => a => new Set( [...a].map( f ) );
const entriesMapIntoObj = f => xs =>
xs.reduce(
(acc, [key, value]) => ({ ...acc, [key]:f(value) })
, {}
);
const mapForObj = f => a =>
entriesMapIntoObj( f )( Object.entries( a ) );
const getType = a => Object.prototype.toString.call(a);
const clone = a => {
const type = getType( a );
return (type === "[object Array]")? a.map( clone )
:(type === "[object Map]")? mapForMap( clone )( a )
:(type === "[object Set]")? mapForSet( clone )( a )
:(type === "[object Object]")? mapForObj( clone )( a )
:(type === "[object Date]")? new Date( a )
: a;
}
基本的にはこれで動くのですが、込み入ったオブジェクトに使うと動作が鈍いときがあります。
mapForMap,mapForSetで新しい配列を作っては捨てているところがあるので、そこをちょっと節約してみました。
const mapForMap = f => a =>
[...a].reduce( (acc,[key, value])=>acc.set(key, f(value)), new Map());
const mapForSet = f => a =>
[...a].reduce((acc, e)=>acc.add(f(e)), new Set());
だいぶ動作が速くなった感があります。
さらにforEachで新たに配列を作らない方法にしてみました。
mapForObjでは直接forEachできませんが、なるべく小さい(entries()でなくkeys())配列を作って回すようにしてみました。
const mapForMap = f => a => {
const b = new Map();
a.forEach((key, value)=>b.set(key,f(value)));
return b;
}
const mapForSet = f => a =>{
const b = new Set();
a.forEach(e=>b.add(f(e)));
return b;
}
const mapForObj = f => a =>{
const b = {};
Object.keys(a).forEach( e => b[e] = f(a[e]) );
return b;
}
const getType = a => Object.prototype.toString.call(a);
const clone = a => {
const type = getType( a );
return (type === "[object Array]")? a.map( clone )
:(type === "[object Map]")? mapForMap( clone )( a )
:(type === "[object Set]")? mapForSet( clone )( a )
:(type === "[object Object]")? mapForObj( clone )( a )
:(type === "[object Date]")? new Date( a )
: a;
}
実用上はこれで十分かな。