Reverse a Hash in Ruby

Reverse a hash in Ruby

You could convert the Hash to an Array, reverse that, and then convert it back to a Hash:

reversed_h = Hash[h.to_a.reverse]

Hash#to_a gives you an array of arrays, the inner arrays are simple [key,value] pairs, then you reverse that array using Array#reverse, and Hash[] converts the [key,value] pairs back into a Hash.

Ruby 2.1 adds an Array#to_h method so you can now say:

reversed_h = h.to_a.reverse.to_h

How to swap keys and values in a hash

Ruby has a helper method for Hash that lets you treat a Hash as if it was inverted (in essence, by letting you access keys through values):

{a: 1, b: 2, c: 3}.key(1)
=> :a

If you want to keep the inverted hash, then Hash#invert should work for most situations:

{a: 1, b: 2, c: 3}.invert
=> {1=>:a, 2=>:b, 3=>:c}

BUT...

If you have duplicate values, invert will discard all but the last occurrence of your values (because it will keep replacing new value for that key during iteration). Likewise, key will only return the first match:

{a: 1, b: 2, c: 2}.key(2)
=> :b

{a: 1, b: 2, c: 2}.invert
=> {1=>:a, 2=>:c}

So, if your values are unique you can use Hash#invert. If not, then you can keep all the values as an array, like this:

class Hash
# like invert but not lossy
# {"one"=>1,"two"=>2, "1"=>1, "2"=>2}.inverse => {1=>["one", "1"], 2=>["two", "2"]}
def safe_invert
each_with_object({}) do |(key,value),out|
out[value] ||= []
out[value] << key
end
end
end

Note: This code with tests is now on GitHub.

Or:

class Hash
def safe_invert
self.each_with_object({}){|(k,v),o|(o[v]||=[])<<k}
end
end

What are some nice ways to reverse a nested hash?

This can be accomplished with a recursive method for inverting the keys of the hash (and values, if desired). For example:

hsh = {{"c"=>"b"}=>"a"}
def recursive_invert(hsh)
hsh.each_with_object({}) do |(k, v), inverted_hsh|
if k.is_a? Hash
k = recursive_invert(k)
end
inverted_hsh[v] = k
end
end
recursive_invert(hsh) # {"a"=>{"b"=>"c"}}

Sorting a complex ruby hash works fine, but it's impossible to reverse, what can I do?

reverse reverses the current order. That means you have to sort first and reverse in a second step:

something.sort.reverse.each { ... }

Or you need to explicitly tell Ruby how to sort:

something.sort_by { |commit_id, _| -commit_id }.each { ... }

How to reverse hash to string conversion?

Looks like you need conversion from JSON to hash. (See DOCS). But, when you do to_s the output is not in pure JSON format. So, you may see I'm using gsub to replace => with :

require 'json'

hash = {"description" => "test"}
=> {"description"=>"test"}

str = hash.to_s.gsub("=>", ":")
=> "{\"description\":\"test\"}"

JSON.parse(str)
=> {"description"=>"test"}

reversing hash.sort_by in ruby

@sortedInternalLinksHash = @countHash.sort_by { |k,v| v }.reverse

Reversing a hash and merging keys in ruby

res = {}
h.each{|k, v| v.each{|vv| res[vv] ||= []; res[vv].push(k)}}

Hash invert in Ruby?

hash = {:key1 => ["a", "b", "c"], :key2 => ["d", "e", "f"]}

first variant

hash.map{|k, v| v.map{|f| {f => k}}}.flatten
#=> [{"a"=>:key1}, {"b"=>:key1}, {"c"=>:key1}, {"d"=>:key2}, {"e"=>:key2}, {"f"=>:key2}]

or

hash.inject({}){|h, (k,v)| v.map{|f| h[f] = k}; h}
#=> {"a"=>:key1, "b"=>:key1, "c"=>:key1, "d"=>:key2, "e"=>:key2, "f"=>:key2}

UPD

ok, your hash is:

hash = {"status"=>{"1"=>["1", "14"], "2"=>["7", "12", "8", "13"]}}
hash["status"].inject({}){|h, (k,v)| v.map{|f| h[f] = k}; h}
#=> {"12"=>"2", "7"=>"2", "13"=>"2", "8"=>"2", "14"=>"1", "1"=>"1"}

Ruby - How to invert a Hash with an array values?

h = {"Book Y"=>["author B", "author C"], "Book X"=>["author A", "author B", "author C"]}

p h.inject(Hash.new([])) { |memo,(key,values)|
values.each { |value| memo[value] += [key] }
memo
}
# => {"author B"=>["Book Y", "Book X"], "author C"=>["Book Y", "Book X"], "author A"=>["Book X"]}

Sort_by in Hash - reverse or not reverse - how?

Reverse does work:

library = {"1"=>{"title"=>"bbb", "money"=>10}, "2"=>{"title"=>"aaa", "money"=>12}}

sort_column = "money"

sort_direction = "asc"
library
.sort_by {|k| k[1][sort_column]}
.tap { |l| l.reverse! if sort_direction == "desc" }
# => [["1", {"title"=>"bbb", "money"=>10}], ["2", {"title"=>"aaa", "money"=>12}]]

sort_direction = "desc"
library
.sort_by {|k| k[1][sort_column]}
.tap { |l| l.reverse! if sort_direction == "desc" }
# => [["2", {"title"=>"aaa", "money"=>12}], ["1", {"title"=>"bbb", "money"=>10}]]


Related Topics



Leave a reply



Submit