Problem
Given a hash, you want to switch the keys and values. That is, you want to create a new hash whose keys are the values of the old hash, and whose values are the keys of the old hash. If the old hash mapped "human" to "wolf;" you want the new hash to map "wolf" to "human."
Solution
The simplest technique is to use the Hash#invert method:
phone_directory = { 'Alice' => '555-1212', 'Bob' => '555-1313', 'Mallory' => '111-1111' } phone_directory.invert # => {"111-1111"=>"Mallory", "555-1212"=>"Alice", "555-1313"=>"Bob"}
Discussion
Hash#invert probably won't do what you want if your hash maps more than one key to the same value. Only one of the keys for that value will show up as a value in the inverted hash:
phone_directory = { 'Alice' => '555-1212', 'Bob' => '555-1313', 'Carol' => '555-1313', 'Mallory' => '111-1111', 'Ted' => '555-1212' } phone_directory.invert # => {"111-1111"=>"Mallory", "555-1212"=>"Ted", "555-1313"=>"Bob"}
To preserve all the data from the original hash, borrow the idea behind Recipe 5.6, and write a version of invert that keeps an array of values for each key. The following is based on code by Tilo Sloboda:
class Hash def safe_invert new_hash = {} self.each do |k,v| if v.is_a? Array v.each { |x| new_hash.add_or_append(x, k) } else new_hash.add_or_append(v, k) end end return new_hash end
The add_or_append method a lot like the method MultivaluedHash#[]= defined in Recipe 5.6:
def add_or_append(key, value) if has_key?(key) self[key] = [value, self[key]].flatten else self[key] = value end end end
Here's safe_invert in action:
phone_directory.safe_invert # => {"111-1111"=>"Mallory", "555-1212"=>["Ted", "Alice"], # "555-1313"=>["Bob", "Carol"]} phone_directory.safe_invert.safe_invert # => {"Alice"=>"555-1212", "Mallory"=>"111-1111", "Ted"=>"555-1212", # => "Carol"=>"555-1313", "Bob"=>"555-1313"}
Ideally, if you called an inversion method twice you'd always get the same data you started with. The safe_invert method does better than invert on this score, but it's not perfect. If your original hash used arrays as hash keys, safe_invert will act as if you'd individually mapped each element in the array to the same value. Call safe_invert twice, and the arrays will be gone.
See Also
Strings
Numbers
Date and Time
Arrays
Hashes
Files and Directories
Code Blocks and Iteration
Objects and Classes8
Modules and Namespaces
Reflection and Metaprogramming
XML and HTML
Graphics and Other File Formats
Databases and Persistence
Internet Services
Web Development Ruby on Rails
Web Services and Distributed Programming
Testing, Debugging, Optimizing, and Documenting
Packaging and Distributing Software
Automating Tasks with Rake
Multitasking and Multithreading
User Interface
Extending Ruby with Other Languages
System Administration