最終更新日:
JavaScript のオブジェクトをディープマージしたい。npm パッケージは存在するけど自力でやりたい。
求める結合結果
以下のように、定数 a と定数 b が定義してあったとする。
const a = {
string: 'hello world',
array: [10, 20, 30, 40],
object: {
boolean: true,
array: ['foo', 'bar', 'baz'],
number: 100,
},
number: 10,
};
const b = {
string: 'Hello World',
array: [30, 40, 50, 60],
object: {
array: [{string: 'hello'}],
number: 500,
},
};
求めている結合結果は以下のようなもので、配列は重複を除いて値が追加され、オブジェクトはどちらかに在るキー/値を残したい。
{
string: 'Hello World',
array: [ 10, 20, 30, 40, 50, 60 ],
object: {
boolean: true,
array: [ 'foo', 'bar', 'baz', { string: 'hello' } ],
number: 500
},
number: 10
}
Object.assign でのマージ
Object.assign を使ってマージすると全て上書きされる
const c = Object.assign(a, b);
console.log(c)
出力結果。a.array は b.array の配列ので上書きされ、a.object.boolean はキーそのものがなくなる。これじゃない。
{
string: 'Hello World',
array: [ 30, 40, 50, 60 ],
object: {
array: [ { string: 'hello' } ],
number: 500
}
number: 10,
}
deep merge するコード
配列の値の重複やソートなんかは、求める結果によって書き換える必要はあるけど、30行くらいで書ける。
function deepMerge(target, ...sources) {
if (sources.length == 0) return target;
const targetIsArray = Array.isArray(target);
// 配列だったら [] オブジェクトだったら {} にする。
const result = Object.assign(targetIsArray ? [] : {}, target);
sources.forEach((source) => {
for (const key in source) {
const targetValue = result[key];
const sourceValue = source[key];
// objectだったら再起させる
if (typeof targetValue === 'object' && typeof sourceValue === 'object') {
result[key] = deepMerge(targetValue, sourceValue);
continue;
}
// 配列だったら重複してなければ追加する
if (targetIsArray) {
if (!result.includes(sourceValue)) {
result.push(sourceValue);
}
continue;
}
// 配列以外は値を上書きしていく
result[key] = sourceValue;
}
});
return result;
}
const a = {
string: 'hello world',
array: [10, 20, 30, 40],
object: {
boolean: true,
array: ['foo', 'bar', 'baz'],
number: 100,
},
number: 10,
};
const b = {
string: 'Hello World',
array: [30, 40, 50, 60],
object: {
array: [{string: 'hello'}],
number: 500,
},
};
const c = deepMerge(a, b);
console.log(c);
実行結果
{
string: 'Hello World',
array: [ 10, 20, 30, 40, 50, 60 ],
object: {
boolean: true,
array: [ 'foo', 'bar', 'baz', {string: 'hello'} ],
number: 500
},
number: 10
}
コメント
コメントを投稿