実例からっ
タイトルがわかりにくいのでやりたいことを実例で示します。
# もとのHash
{"akan"=>["10", "201"], "areha"=>["201"], "okashi"=>["303", "201", "10"]}
# 入れ替え後
=> {"10"=>["akan", "okashi"], "201"=>["akan", "areha", "okashi"], "303"=>["okashi"]}
上記のように1つのキーに複数の値がぶら下がっていて,その値同士が重複しているとします。上の例では,"akan"=>"201","areha"=>"201","okashi"=>"201"と"akan"=>"10","okashi"=>"10"が重複しています。
複数対複数であるということを認識しながら,これらのキーと値を入れ替えると,"入れ替え後"のようになります。ちょいと探したところすぐにいいメソッドが見つからなかったので,メソッドを作りました。
メソッド
def safe_invert_d_hash(orig_hash)
orig_array = []
orig_hash.each{|elem|
if elem[1].length != 1
elem[1].each{|youso|
orig_array << [youso,elem[0]]
}
else
orig_array << [elem[1].join(""),elem[0]]
end
}
result = Hash.new{|h,key| h[key] = []}
orig_array.each{|key,value|
result[key] << value
}
result
end
最初,配列(orig_array)を作って,そこに一対一になるようにデータを入れていきます。この時にキーと値の順序を交換しています。複数の値は1つ1つ取り出しています。
ここまでの処理で上記の例だと,[["10", "akan"], ["201", "akan"], ["201", "areha"], ["303", "okashi"], ["201", "okashi"], ["10", "okashi"]]
となっています。
次に先ほど作った配列から,ハッシュ(result)を作って,最初の要素をkey,後の要素をvalueとすればおしまいです。Have fun! というか,もっといい方法(修正)があったら教えて下さい。#あと,値が配列の形じゃないとこのメソッド使えないです。ごめんなさい。
実行結果
irb(main):538:0* h
=> {"akan"=>["10", "201"], "areha"=>["201"], "okashi"=>["303", "201", "10"]}
irb(main):539:0> safe_invert_d_hash(h)
=> {"10"=>["akan", "okashi"], "201"=>["akan", "areha", "okashi"], "303"=>["okashi"]}
あ,最初の例そのままですね。ええ。
Reference
Rubyレシピブック 第2版 268の技
こちらのsafe_invertを参照しています。
最後にきっかけ
同義語の対応付けの処理をしていて,多(by DB_A)対多(by DB_B)のIDを関連付ける必要がありました。片方のデータベース(DB_A)からしか,対応データが取得できない状況だったが,取得したいのは,(DB_B)からの対応付けだったのでこんなのを作りました。しかし,あれだ,例が,"あかん,あれは,おかし"だったのは,あれですな,あれ。あとデータ量多い時にはRubyじゃないほうがいいですよね,うん。