jsのライブラリdeepmergeの使い方。ネストしたオブジェクトをマージする方法と注意点について
##目次
##deepmergeとは? オブジェクトを結合できるnpmのライブラリ。 インポートして使用する。
import merge from 'deepmerge'
インポート名はなんでもいい。ここではmergeを使用。
**▼deepmergeの注意点**
結合したときに、元のデータの形によって上書きされることもある。パターン別に内容を確認していく。
##deepmergeのパターン
- 上書きされる場合
- プロパティ名が同じ、かつ、どちらか一方の値が配列でない
- 上書きにならない場合
- プロパティ名が異なる
- プロパティの値がどちらも配列
- 配列内でプロパティ名が重複
##1. 上書きされる場合
まずは上書きされるパターンから。
同じプロパティ名で値が配列でない場合、マージ結果は上書きされたものになる。
let obj1 = { id: 10 },
let obj2 = { id: 20 },
console.log(merge(obj1, obj2))
//出力
{ "id": 20 }
出力は{ "id": 20 }
となる。第1引数の値は上書きされて消える。
###▼上書きの例3 プロパティの値が入れ子になっていても、配列でなければこちらも上書きされる。
let obj1 = { id: {b:10} },
let obj2 = { id: {b:20} },
console.log(merge(obj1, obj2))
//出力
{ "id": { "b": 20 } }
###▼上書きの例3 片方のみが配列の場合も上書きされる。
let obj1 = { id: [10] },
let obj2 = { id: 20 },
console.log(merge(obj1, obj2))
//出力
{ "id": 20 }
let obj1 = { id: 10 },
let obj2 = { id: [20] },
console.log(merge(obj1, obj2))
//出力
{ "id": [20] }
プロパティ名が同じ、かつ、どちらか一方の値が配列でない場合は、後ろの引数の値でマージされるので注意が必要。
##2. 上書きにならない場合
###2-1. プロパティ名が異なる
プロパティ名が異なる場合は上書きにならず、意図した通りにマージしてくれる。
let obj1 = { x: 10 },
let obj2 = { y: 20 },
console.log(merge(obj1, obj2))
//出力
{ "x": 10, "y": 20 }
###2-2. プロパティの値がどちらも配列 プロパティの値がどちらも配列の場合は結合できる。
let obj1 = { a: [10] },
let obj2 = { a: [20] },
console.log(merge(obj1, obj2))
//出力
{ "a": [ 10, 20 ] }
###複数の場合
複数のプロパティが入り混じっている場合は、どちらも配列の場合とプロパティ名が異なる場合は結合。それ以外は上書きとなる。
let obj1 = { a: [10], b:30, c:50},
let obj2 = { a: [20], b:400, d:600},
console.log(merge(obj1, obj2))
//出力
{ "a": [ 10, 20 ], "b": 400, "c": 50, "d": 600 }
aは配列どおしなので結合。
bは配列でないので上書き。
c, dはプロパティ名が重複していないので結合となる。
##2-3. 配列内でプロパティ名が重複
プロパティ名が被っていても、配列の中であれば上書きなしでそれぞれ結合される。
let obj1 = { a: [{ x: 1 }] },
let obj2 = { a: [{ x: 2 }] },
console.log(merge(obj1, obj2))
//出力
{ "a": [ { "x": 1 }, { "x": 2 } ] }
プロパティaの中の配列で、プロパティxが重複しているが、それぞれ個別に結合される。
###複数の場合
let obj1 = { b:[{x:2, y:3}] },
let obj2 = { b:[{x:4, y:5}] },
console.log(merge(obj1, obj2))
//出力
{ "b": [ { "x": 2, "y": 3 }, { "x": 4, "y": 5 } ] }
##配列の結合(オブジェクトでない場合)
オブジェクトではなく配列をマージすると、ただの結合になる。
let arr1 = [1,2,[3]],
let arr2 = [9,8,[7]],
console.log(merge(arr1, arr2))
//出力
[ 1, 2, [ 3 ], 9, 8, [ 7 ] ]
##公式ページの結合事例を解読してみる
最後に公式ページの結合事例を見てみる。
const x = {
foo: { bar: 3 },
array: [{
does: 'work',
too: [ 1, 2, 3 ]
}]
},
const y = {
foo: { baz: 4 },
quux: 5,
array: [{
does: 'work',
too: [ 4, 5, 6 ]
}, {
really: 'yes'
}]
}
・プロパティfooの中身はbarとbazでプロパティ名が異なるためどちらも残る。
・arrayの中も配列なのでどちらも残る。配列の中のプロパティ名が同じでも個別に出力される。
・quuxはプロパティ名が固有(異なる)ためそのまま出力される。
const output = {
foo: {
bar: 3,
baz: 4
},
array: [{
does: 'work',
too: [ 1, 2, 3 ]
}, {
does: 'work',
too: [ 4, 5, 6 ]
}, {
really: 'yes'
}],
quux: 5
}
##Vueの確認用ソースコード
<template>
<div>
<p>{{obj}}</p>
<v-btn @click="deepMerge(obj1, obj2)">
(obj1, obj2)
</v-btn>
<v-btn @click="deepMerge(obj3, obj4)">
(obj3, obj4)
</v-btn>
<v-btn @click="deepMerge(obj5, obj6)">
(obj5, obj6)
</v-btn>
<v-btn @click="deepMerge(obj7, obj8)">
(obj7, obj8)
</v-btn>
<v-btn @click="deepMerge(arr1, arr2)">
(arr1, arr2)
</v-btn>
<v-btn @click="deepMerge(val1, val2)">
(val1, val2)
</v-btn>
<v-btn @click="deepMerge(x, y)">
(x, y)
</v-btn>
</div>
</template>
<script>
import merge from 'deepmerge'
export default {
data(){
return{
obj: {},
obj1: { b:[{x:2, y:3}] },
obj2: { b:[{x:4, y:5}] },
obj3: { id: {b:10} },
obj4: { id: {b:20} },
obj5: { x: 10 },
obj6: { y: 20 },
obj7: { a: [10], b:30, c:50},
obj8: { a: [20], b:400, d:600},
arr1: [1,2,[3]],
arr2: [9,8,[7]],
val1: {width: '50%'},
val2: {width: '33%'},
x:{
foo: { bar: 3 },
array: [{
does: 'work',
too: [ 1, 2, 3 ]
}]
},
y:{
foo: { baz: 4 },
quux: 5,
array: [{
does: 'work',
too: [ 4, 5, 6 ]
}, {
really: 'yes'
}]
}
}
},
methods:{
deepMerge(a, b){
this.obj = merge(a,b)
}
}
}
</script>
一つづつパターンを分けていくと、どういったパターンで上書きされるかがわかる。