【Vue】deepmergeの使い方。ネストしたオブジェクトをマージする。

jsのライブラリdeepmergeの使い方。ネストしたオブジェクトをマージする方法と注意点について


##目次

1. [deepmergeとは？](#deepmergeとは)
2. [上書きされる場合](#1-上書きされる場合)
3. [上書きにならない場合](#2-上書きにならない場合)
    1. [プロパティ名が異なる](#2-1-プロパティ名が異なる)
    2. [プロパティの値がどちらも配列](#2-2-プロパティの値がどちらも配列) 
    3. [配列内でプロパティ名が重複](#2-3-配列内でプロパティ名が重複)
4. [配列の結合（オブジェクトでない場合）](#配列の結合オブジェクトでない場合)
5. [公式ページの結合事例を解読してみる](#公式ページの結合事例を解読してみる)
6. [Vueの確認用ソースコード](#vueの確認用ソースコード)




<br>
##deepmergeとは？
オブジェクトを結合できるnpmのライブラリ。
インポートして使用する。

```js:インポート方法

import merge from 'deepmerge'
```

インポート名はなんでもいい。ここではmergeを使用。


＞[npm公式 deepmerge](https://www.npmjs.com/package/deepmerge)


<br>
**▼deepmergeの注意点**

結合したときに、元のデータの形によって上書きされることもある。パターン別に内容を確認していく。


##deepmergeのパターン

1. 上書きされる場合
    1. プロパティ名が同じ、かつ、どちらか一方の値が配列でない
2. 上書きにならない場合
    1. プロパティ名が異なる
    2. プロパティの値がどちらも配列 
    3. 配列内でプロパティ名が重複


<br>

##1. 上書きされる場合
まずは上書きされるパターンから。

同じプロパティ名で値が配列でない場合、マージ結果は上書きされたものになる。

```js:上書きの例１
let obj1 = { id: 10 },
let obj2 = { id: 20 },

console.log(merge(obj1, obj2))

//出力
{ "id": 20 }
```

出力は`{ "id": 20 }`となる。第１引数の値は上書きされて消える。


<br>
###▼上書きの例3
プロパティの値が入れ子になっていても、配列でなければこちらも上書きされる。

```js:上書きの例2
let obj1 = { id: {b:10} },
let obj2 = { id: {b:20} },

console.log(merge(obj1, obj2))

//出力
{ "id": { "b": 20 } }
```


<br>
###▼上書きの例3
片方のみが配列の場合も上書きされる。

```js:上書きの例3
let obj1 = { id: [10] },
let obj2 = { id: 20 },

console.log(merge(obj1, obj2))

//出力
{ "id": 20 }
```

```js:上書きの例3'
let obj1 = { id: 10 },
let obj2 = { id: [20] },

console.log(merge(obj1, obj2))

//出力
{ "id": [20] }
```

プロパティ名が同じ、かつ、どちらか一方の値が配列でない場合は、後ろの引数の値でマージされるので注意が必要。



<br>
##2. 上書きにならない場合

###2-1. プロパティ名が異なる
プロパティ名が異なる場合は上書きにならず、意図した通りにマージしてくれる。


```js:上書きなし例１
let obj1 = { x: 10 },
let obj2 = { y: 20 },

console.log(merge(obj1, obj2))

//出力
{ "x": 10, "y": 20 }
```


<br>
###2-2. プロパティの値がどちらも配列
プロパティの値がどちらも配列の場合は結合できる。

```js:上書きなし例１
let obj1 = { a: [10] },
let obj2 = { a: [20] },

console.log(merge(obj1, obj2))

//出力
{ "a": [ 10, 20 ] }
```


<br>

###複数の場合
複数のプロパティが入り混じっている場合は、どちらも配列の場合とプロパティ名が異なる場合は結合。それ以外は上書きとなる。

```js:
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はプロパティ名が重複していないので結合となる。



<br>

##2-3. 配列内でプロパティ名が重複
プロパティ名が被っていても、配列の中であれば上書きなしでそれぞれ結合される。

```js:
let obj1 = { a: [{ x: 1 }] },
let obj2 = { a: [{ x: 2 }] },

console.log(merge(obj1, obj2))

//出力
{ "a": [ { "x": 1 }, { "x": 2 } ] }
```

プロパティaの中の配列で、プロパティxが重複しているが、それぞれ個別に結合される。


###複数の場合

```js:
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 } ] }
```


<br>
##配列の結合（オブジェクトでない場合）

オブジェクトではなく配列をマージすると、ただの結合になる。

```js:配列
let arr1 = [1,2,[3]],
let arr2 = [9,8,[7]],

console.log(merge(arr1, arr2))

//出力
[ 1, 2, [ 3 ], 9, 8, [ 7 ] ]
```


<br>

##公式ページの結合事例を解読してみる

最後に公式ページの結合事例を見てみる。

```js:結合する要素xとy
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はプロパティ名が固有（異なる）ためそのまま出力される。



```js:結合結果
const output = {
    foo: {
        bar: 3,
        baz: 4
    },
    array: [{
        does: 'work',
        too: [ 1, 2, 3 ]
    }, {
        does: 'work',
        too: [ 4, 5, 6 ]
    }, {
        really: 'yes'
    }],
    quux: 5
}
```



<br>

##Vueの確認用ソースコード

▼ブラウザの表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/852f59c5-4f0d-0eee-5591-957416e17d1f.png)


```vue:.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>
```


<br>
一つづつパターンを分けていくと、どういったパターンで上書きされるかがわかる。