问题
How would I reverse the elements in the hash, keeping the same values and keys, but reversing their order in the hash.
Like so:
{ "4" => "happiness", "10" => "cool", "lala" => "54", "1" => "spider" }
And convert that to:
{ "1" => "spider", "lala" => "54", "10" => "cool", "4" => "happiness" }
Or, perhaps I could run a each loop backwards, starting from the last element in the hash, rather than the first?
回答1:
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
回答2:
In Ruby 2.1+ you can combine reverse_each and to_h:
{foo: 1, bar: 2}.reverse_each.to_h
#=> {:bar=>2, :foo=>1}
回答3:
hash = { "4" => "happiness", "10" => "cool", "lala" => "54", "1" => "spider" }
reversed_hash = Hash[hash.to_a.reverse]
回答4:
h = { "4" => "happiness", "10" => "cool", "lala" => "54", "1" => "spider" }
p Hash[h.reverse_each.map{|e| e}]
#=> {"1"=>"spider", "lala"=>"54", "10"=>"cool", "4"=>"happiness"}
But this leaves a bad taste (just like the other answers, which work fine just like this one). If you have to do this, it could be an indication that a Hash was not the best choice.
回答5:
reversed_h = Hash[h.to_a.collect(&:reverse)]
回答6:
In pure ruby, you can do it by hash.map(&:reverse).to_h or hash.reverse_each.to_h
In rails, you can do it by hash.invert
回答7:
Alternatively, you can use reduce and merge to add the item to the front of a new hash:
hash = { "4" => "happiness", "10" => "cool", "lala" => "54", "1" => "spider" }
hash.reduce({}){ |memo, object| Hash[*object].merge(memo) }
but, that's crazy :D
回答8:
In Ruby 1.8.7, the order of elements in a hash is documented to be not under our control, so none of the above methods work. In Ruby 1.9.3, things work and are documented in the way that the other answers rely upon.
$ irb1.8
h = { "4" => "happiness", "10" => "cool", "lala" => "54", "1" => "spider" }
Hash[h.to_a().reverse()]
=> {"lala"=>"54", "1"=>"spider", "10"=>"cool", "4"=>"happiness"}
quit
$ irb1.9.1
h = { "4" => "happiness", "10" => "cool", "lala" => "54", "1" => "spider" }
Hash[h.to_a().reverse()]
=>{"1"=>"spider", "lala"=>"54", "10"=>"cool", "4"=>"happiness"}
The Ruby 1.8.7 way was ingrained so firmly for me that I misunderstood the question for quite some time. I thought it requested a way to Hash#invert: ie to transform the hash such that the range maps to the domain. That method discards duplicates. Luís Ramalho proffers a method that doesn't, but it's a bit clunky. This is a little shorter:
$ irb
def invertWithDuplicates(original)
inverse = Hash.new() { |hash, key| hash[key] = []; }
original.each_pair() { |key, value| inverse[value].push(key); }
return inverse
end
h = { "4" => "happiness", "10" => "cool", "lala" => "54", "1" => "cool" }
invertWithDuplicates(h)
=> {"happiness"=>["4"], "cool"=>["1", "10"], "54"=>["lala"]}
Sorry to drift away from the OP's intended topic, though I submit that this does fit the post's title "Reverse a hash in Ruby".
回答9:
if need:
hash = {:a => :x, :b => :y, :c => :y, :d => :z}
to:
{:x => [:a], :y => [:b, c], :z => [:d] }
can:
h={};hash.to_a.each{|e|h[e[1]]||=[];h[e[1]]<<e[0]};h
来源:https://stackoverflow.com/questions/10874356/reverse-a-hash-in-ruby