RubyでネストしているHashをいい感じにmergeする
結論
deep_mergeを使う。
やりたいこと
こんな感じでネストされた Hash データがあるとする。
def nested_hash_data
  {
    'target1' => {
      'category1_1' => {
        'resource1_1_1' => {
          'parameter' => 'hogehoge',
        },
        'resource1_1_2' => {
          'parameter' => 'hogehoge',
        },
      },
      'category1_2' => {
        'resource1_2_1' => {
          'parameter' => 'hogehoge',
        },
      },
    },
  }
end
「ある条件のときに特定の resource を追加したいよ〜」という要件が出てくる。
このとき追加差分がわかりやすいように元データと同じ構造で定義していたとする。
def ex_data
  {
    'target1' => {
      'category1_1' => {
        'resource1_1_3' => {
          'parameter' => 'hogehoge',
        },
      },
      'category1_2' => {
        'resource1_2_2' => {
          'parameter' => 'hogehoge',
        },
      },
    },
  }
end
A と B を単純に merge して、各 category に resource1_1_3 と resource1_2_2 を追加したい。期待値としては ↓ な感じ。
{
  'target1' => {
    'category1_1' => {
      'resource1_1_1' => {
        'parameter' => 'hogehoge',
      },
      'resource1_1_2' => {
        'parameter' => 'hogehoge',
      },
      'resource1_1_3' => {
        'parameter' => 'hogehoge',
      },
    },
    'category1_2' => {
      'resource1_2_1' => {
        'parameter' => 'hogehoge',
      },
      'resource1_2_2' => {
        'parameter' => 'hogehoge',
      },
    },
  },
}
課題感
A と B を単純に merge メソッドに渡すと期待通りにマージされない。
# nested_hash_data.merge(ex_data)
{
  'target1' => {
    'category1_1' => {
      'resource1_1_3' => {
        'parameter' => 'hogehoge',
      },
    },
    'category1_2' => {
      'resource1_2_2' => {
        'parameter' => 'hogehoge',
      },
    },
  },
}
解決方法
deep_mergeを使う。
$ gem install 'deep_merge'
require 'deep_merge'
nested_hash_data.deep_merge(ex_data)
gem を別途入れないとネストされた Hash データをいい感じに merge できないみたい。
余談
今回は deep_merge を見ていたけど activesupport を入れるといろいろと便利なメソッドが提供されていそう。
環境
$ ruby --version
ruby 2.7.5p203 (2021-11-24 revision f69aeb8314) [arm64-darwin21]
使っている deep_merge が Ruby の require version を >=0 としているので何でも動くはず。