# Inverting a Hash

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
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.

• Recipe 5.5, "Using an Array or Other Modifiable Object as a Hash Key"
• "True Inversion of a Hash in Ruby," by Tilo Sloboda (http://www.unixgods.org/~tilo/Ruby/invert_hash.html)
• The Facets library defines a Hash#inverse method much like safe_invert

Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Similar book on Amazon