はじめに
StringとSymbolで同一名のKeyを持つHashはやめよう
{'id' => 10, :id => 11, 'name' => 'AAA', :name => 'BBB'}
遭遇
ruby2.4+rails Hash.transform_keys!が特定の状況でsymbolize_keys(非破壊)と互換のない挙動をする
ruby2.5.2+rails ruby2.5で同関数が実装された事で正常に動作する
確認
ruby2.4+rails
symbolが先の場合
[1] pry(main)> {:id => 10, 'id' => 11, :name => 'AAA', 'name' => 'BBB'}.symbolize_keys => {:id=>11, :name=>"BBB"} [2] pry(main)> {:id => 10, 'id' => 11, :name => 'AAA', 'name' => 'BBB'}.symbolize_keys! => {:id=>11, :name=>"BBB"}
symbolが後の場合
[1] pry(main)> {'id' => 10, :id => 11, 'name' => 'AAA', :name => 'BBB'}.symbolize_keys => {:id=>11, :name=>"BBB"} [2] pry(main)> {'id' => 10, :id => 11, 'name' => 'AAA', :name => 'BBB'}.symbolize_keys! => {:id=>10, :name=>"AAA"}
ruby2.5.2+rails
symbolが先の場合
[1] pry(main)> {:id => 10, 'id' => 11, :name => 'AAA', 'name' => 'BBB'}.symbolize_keys => {:id=>11, :name=>"BBB"} [2] pry(main)> {:id => 10, 'id' => 11, :name => 'AAA', 'name' => 'BBB'}.symbolize_keys! => {:id=>11, :name=>"BBB"}
symbolが後の場合
[1] pry(main)> {'id' => 10, :id => 11, 'name' => 'AAA', :name => 'BBB'}.symbolize_keys => {:id=>11, :name=>"BBB"} [2] pry(main)> {'id' => 10, :id => 11, 'name' => 'AAA', :name => 'BBB'}.symbolize_keys! => {:id=>11, :name=>"BBB"}
原因
該当箇所はここ
self[yield(key)] = delete(key)
の部分、
'id'
で取得した物を:id
に対して代入し、symbolで保持していた値が失われている為。
def transform_keys! return enum_for(:transform_keys!) { size } unless block_given? keys.each do |key| self[yield(key)] = delete(key) end self end def symbolize_keys! transform_keys! { |key| key.to_sym rescue key } end
おわりに
StringとSymbolで同一名のKeyを持つHashはやめよう
Source
Transcribed from Qiita.