[Ruby] 階層の深いHashのmerge（deep_merge）と、そのコード簡略化


# 困ったこと

Hash{}のmergeメソッドが、同一階層内の別keyデータを保持してくれない。

```
irb(main):001:0> h = {a:{aa:11, ab:11}}
irb(main):002:0> h.merge({a:{aa:99}})
    ==>{:a=>{:aa=>99}}
```
`==> {:a=>{:aa=>99, :ab=>11}}` になってほしい

# TL;DR

↓ソースをコピペすればOK

```
class Hash
  def deep_merge(*others)
    self.clone.deep_merge!(*others)
  end

  def deep_merge!(*others)
    _child_merge = -> (_, self_val, other_val){
      if self_val.instance_of?(Hash) && other_val.instance_of?(Hash)
        self_val.merge other_val, &_child_merge
      else
        other_val
      end
    }
    self.merge! *others, &_child_merge
  end
end
```

↓使用例

```
$ irb
irb(main):001:1* class Hash
irb(main):002:2*   def deep_merge(*others)
irb(main):003:2*     self.clone.deep_merge!(*others)
irb(main):004:1*   end
irb(main):005:1*   
irb(main):006:2*   def deep_merge!(*others)
irb(main):007:2*     _child_merge = -> (_, self_val, other_val){
irb(main):008:3*     if self_val.instance_of?(Hash) && other_val.instance_of?(Hash)
irb(main):009:3*       self_val.merge other_val, &_child_merge
irb(main):010:3*     else
irb(main):011:3*       other_val
irb(main):012:2*     end
irb(main):013:1*   }
irb(main):014:1*   self.merge! *others, &_child_merge
irb(main):015:0* end
irb(main):016:-> end
    ==>:deep_merge!
irb(main):017:0> h = {a:{aa:11, ab:11}}
irb(main):018:0> h.deep_merge({a:{aa:99}}) #deep_mergeは元データを変更しない
    ==>{:a=>{:aa=>99, :ab=>11}}
irb(main):019:0> h
    ==>{:a=>{:aa=>11, :ab=>11}}
irb(main):020:0> h.deep_merge!({a:{aa:99}}) #deep_merge!は元データも変更する
    ==>{:a=>{:aa=>99, :ab=>11}}
irb(main):021:0> h
    ==>{:a=>{:aa=>99, :ab=>11}}
```

# 説明

どうやってソースを組み立てたかと、コード簡略化について説明していきます。

## 困ったことの解消

まずHashのmergeメソッドについて見てみます。

このメソッドは、同一階層内でなければ、複数の値を保持してくれます。

```
irb(main):001:0> h1 = { "a" => 100, "b" => 200 }
irb(main):002:0> h2 = { "c" => 300, "d" => 400 }
irb(main):003:0> h1.merge(h2)
    ==>{"a"=>100, "b"=>200, "c"=>300, "d"=>400}
```

また以下のように、
keyが重複した場合のみ、merge元データとmerge先データでイテレータ処理ができます。

```
irb(main):031:0> h1 = { "a" => 100, "b" => 200 }
irb(main):032:0> h2 = { "b" => 246, "c" => 300 }
irb(main):033:0> h1.merge(h2) {|key, oldval, newval| newval - oldval}
    ==>{"a"=>100, "b"=>46,  "c"=>300}
```

そして、イテレータのなかでは１階層落ちたデータとなります。

```
irb(main):034:0> h1 = { "a" => 100, "b" => {"bb"=>210, "bc"=>220} }
irb(main):035:0> h2 = { "b" => {"bd"=>230}, "c" => 300 }
irb(main):036:1* h1.merge(h2) {|key, oldval, newval|
irb(main):037:1*   p oldval
irb(main):038:1*   p newval
irb(main):039:0> }
{"bb"=>210, "bc"=>220}
{"bd"=>230}
```

つまり、「keyが重複した場合」にイテレータ内で「再びmerge」すれば、
階層が深くなっても値を落とさずに処理できます。

```
irb(main):040:0> h1 = { "a" => 100, "b" => {"bb"=>210, "bc"=>220} }
irb(main):041:0> h2 = { "b" => {"bd"=>230}, "c" => 300 }
irb(main):042:0> h1.merge(h2) {|key, oldval, newval| oldval.merge(newval)}
    ==>{"a"=>100, "b"=>{"bb"=>210, "bc"=>220, "bd"=>230}, "c"=>300} # データが保持されてる！
```

やりたかった処理（＝同一階層内の別keyデータを保持）が
実現できるメドが立ちました。

## 再帰処理の追加

ただ１階層ではダメで、階層を深堀りしていく必要があります。

そこで、処理をメソッド化し、
メソッド内部で、再帰的にイテレータ処理を行うようにします。

また、再帰的に処理を行うなかで、片方がHashでない場合は、
merge先データ(=newval)を正として上書きするので、イテレータ処理は不要となります。

これらを反映するとこうなります。

```
irb(main):043:1* def child_merge(key, oldval, newval)
irb(main):044:2*   if oldval.instance_of?(Hash) && newval.instance_of?(Hash)
irb(main):045:2*     return oldval.merge(newval) {|key, oldval, newval| child_merge(key, oldval, newval)}
irb(main):046:2*   else
irb(main):047:2*     return newval
irb(main):048:1*   end
irb(main):049:0> end
    ==>:child_merge
irb(main):050:0> h1 = { "a" => 100, "b" => {"bb"=>{"bbb"=>210}, "bc"=>220} }
irb(main):051:0> h2 = { "b" => {"bb"=>{"bbe"=>240}, "bd"=>230}, "c" => 300 }
irb(main):052:0> h1.merge(h2) {|key, oldval, newval| child_merge(key, oldval, newval)}
    ==>{"a"=>100, "b"=>{"bb"=>{"bbb"=>210, "bbe"=>240}, "bc"=>220, "bd"=>230}, "c"=>300}
```

## リファクタ

ここですこし、リファクタをします。

* 簡易な処理なのでreturnを省略

```
def child_merge(key, oldval, newval)
  if oldval.instance_of?(Hash) && newval.instance_of?(Hash)
    oldval.merge(newval) {|key, oldval, newval| child_merge(key, oldval, newval)}
  else
    other_val
  end
end
```

* 使わないキーは`_`にして省略

```
def child_merge(_, oldval, newval)
```

* この変数3つ`|key, oldval, newval|`が長いので、lambda関数に変更  
（意外とこの、"引数"+"ブロック処理"のlambda記法が見つからなくて、めちゃくちゃ探しました）

```
child_merge = -> (_, oldval, newval){
  if oldval.instance_of?(Hash) && newval.instance_of?(Hash)
    oldval.merge newval, &child_merge
  else
    newval
  end
}
```

けっこうシンプルになりました。

## Hashクラスへの適用

最後に、（Rubyの大きな特徴のひとつですが）classにメソッドを直接overrideできるので、
そちらを利用して、よりコードをシンプルにしていきます。

メソッドの変数名も、Hash#mergeに合わせて `self_val`, `other_val`にします。

```
class Hash
  def deep_merge(other_h)
    _child_merge = -> (_, self_val, other_val){
      if self_val.instance_of?(Hash) && other_val.instance_of?(Hash)
        self_val.merge other_val, &_child_merge
      else
        other_val
      end
    }
    self.merge other_h, &_child_merge
  end
end
```

（private変数は`_xxx`にしてます。ruby的には意味ないです。ただの好みです）

また、Hash#mergeは複数ハッシュも対応しているので、その対応も追加します。

```
class Hash
  def deep_merge(*others)
    (省略)
    self.merge *others, &_child_merge
  end
end
```

元データも変更する、Hash#merge!にも対応します。

```
class Hash
  def deep_merge!(*others)
    (省略)
    self.merge! *others, &_child_merge
  end
end
```

そして最後に、通常のdeep_mergeも追加して、完成です。

```
class Hash
  def deep_merge(*others)
    self.clone.deep_merge!(*others)
  end

  def deep_merge!(*others)
    _child_merge = -> (_, self_val, other_val){
      if self_val.instance_of?(Hash) && other_val.instance_of?(Hash)
        self_val.merge other_val, &_child_merge
      else
        other_val
      end
    }
    self.merge! *others, &_child_merge
  end
end
```

# 使用方法

TL;DRの使用例にもあるように、
Hashクラスの変数であれば、メソッドを直接呼び出せるようになります。

```
irb(main):001:1* class Hash
(省略)
irb(main):016:-> end
    ==>:deep_merge!
irb(main):017:0> h = {a:{aa:11, ab:11}}
irb(main):018:0> h.deep_merge({a:{aa:99}})
    ==>{:a=>{:aa=>99, :ab=>11}}
irb(main):019:0> h
    ==>{:a=>{:aa=>11, :ab=>11}}
irb(main):020:0> h.deep_merge!({a:{aa:99}})
    ==>{:a=>{:aa=>99, :ab=>11}}
irb(main):021:0> h
    ==>{:a=>{:aa=>99, :ab=>11}}
irb(main):022:0> h.deep_merge({a:{ac:11}}, {b:1})
    ==>{:a=>{:aa=>99, :ab=>11, :ac=>11}, :b=>1}
irb(main):023:0> h.deep_merge({a:{ac:11}}, {b:1}, {b:9})
    ==>{:a=>{:aa=>99, :ab=>11, :ac=>11}, :b=>9}
```

以上。

# 参考

https://docs.ruby-lang.org/ja/latest/method/Hash/i/merge.html

https://stackoverflow.com/questions/102165/understanding-the-behaviour-of-inject-used-with-a-lambda-in-ruby