Ruby 2.7.0がリリースされ、パターンマッチングが入ったということで差分データを元にデータを更新するような処理を書いてみる

実装

# hash.rb
require 'active_support/all'

class Hash
  def merge_diff!(diff)
    diff.each do |k, v|
      case [self[k], v]
      in [Hash, Hash] then self[k].merge_diff!(v)
      in [_, Hash] then self[k] = {}.merge_diff!(v)
      in [_, NilClass] then delete(k)
      else self[k] = v
      end
    end
    self
  end

  def merge_diff(diff)
    deep_dup.merge_diff!(diff)
  end
end

テスト

# test.rb
require './hash.rb'
require 'test/unit'

class TestMergeDiff < Test::Unit::TestCase
  def test_merge_diff!
    origin = {
      a: 1,
      b: 2,
      c: { ca: 1 },
      d: { da: 1, db: { dba: 1 } }    
    }

    diff = { 
      a: 5, 
      d: { da: 10 },
      e: 5
    }
    
    answer = { 
      a: 5,
      b: 2,
      c: { ca: 1 },
      d: { da: 10, db: { dba: 1 } },
      e: 5
    }
    
    assert_equal answer, origin.merge_diff!(diff)
  end

  def test_merge_diff
    origin = {
      a: 1,
      b: 2,
      c: { ca: 1 },
      d: { da: 1, db: { dba: 1 } }    
    }

    diff = { 
      c: { ca: 10, cb: { cba: 1 } }, 
      d: { db: { dba: 2, dbb: 5 } } 
    }

    answer = {
      a: 1,
      b: 2,
      c: { ca: 10, cb: { cba: 1 } },
      d: { da: 1, db: { dba: 2, dbb: 5 } }    
    }

    assert_equal answer, origin.merge_diff(diff)
  end
end

パターンマッチング非常に便利。

追記

ActiveSupportにHash#deep_mergeという今回実装したものと同じものが存在するので無駄な実装になった…Ruby力が足りてない…