Обратный хеш в Ruby

Как бы я перевернул элементы в хэше, сохранив те же значения и ключи, но изменив их порядок в хэше.

Вот так:

{ "4" => "happiness", "10" => "cool", "lala" => "54", "1" => "spider" }

И преобразовать это в:

{ "1" => "spider", "lala" => "54", "10" => "cool", "4" => "happiness" }

Или, может быть, я мог бы запустить each цикл в обратном порядке, начиная с последнего элемента в хэше, а не с первого?

8 ответов

Решение

Вы можете преобразовать Hash в Array, перевернуть его, а затем преобразовать обратно в Hash:

reversed_h = Hash[h.to_a.reverse]

Hash#to_a дает вам массив массивов, внутренние массивы просты [key,value] пар, затем вы обращаете этот массив с помощью Array#reverse, а также Hash[] преобразует [key,value] пары обратно в хэш.

Ruby 2.1 добавляет Array#to_h метод, так что теперь вы можете сказать:

reversed_h = h.to_a.reverse.to_h

В Ruby 2.1+ вы можете комбинировать reverse_each а также to_h:

{foo: 1, bar: 2}.reverse_each.to_h
#=> {:bar=>2, :foo=>1}

В чистом рубине вы можете сделать это hash.map(&:reverse).to_h или же hash.reverse_each.to_h

В рельсах вы можете сделать это hash.invert

hash = { "4" => "happiness", "10" => "cool", "lala" => "54", "1" => "spider" }
reversed_hash = Hash[hash.to_a.reverse]
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"}

Но это оставляет неприятный вкус (как и другие ответы, которые отлично работают, как этот). Если вам нужно сделать это, это может указывать на то, что хэш был не лучшим выбором.

reversed_h = Hash[h.to_a.collect(&:reverse)]

Кроме того, вы можете использовать reduce а также merge чтобы добавить элемент в начало нового хэша:

hash = { "4" => "happiness", "10" => "cool", "lala" => "54", "1" => "spider" }
hash.reduce({}){ |memo, object| Hash[*object].merge(memo) }

но это безумие:D

В Ruby 1.8.7 задокументировано, что порядок элементов в хэше не находится под нашим контролем, поэтому ни один из вышеперечисленных методов не работает. В Ruby 1.9.3 все работает и документируется так, как полагаются другие ответы.

$ irb1.8
h = { "4" => "счастье", "10" => "круто", "лала" => "54", "1" => "паук" }
Хэш [h.to_a(). Обратное ()]
=> {"lala"=>"54", "1"=>"паук", "10" => "круто", "4"=>"счастье" }
уволиться
$ irb1.9.1
h = { "4" => "счастье", "10" => "круто", "лала" => "54", "1" => "паук" }
Хэш [h.to_a(). Обратное ()]
=>{"1"=>"паук", "лала"=>"54", "10"=>"круто", "4"=>"счастье" }

Способ Ruby 1.8.7 был укоренен для меня настолько прочно, что я довольно долго не понимал вопрос. Я думал, что он запросил способ Hash # invert: т.е. преобразовать хеш так, чтобы диапазон соответствовал домену. Этот метод отбрасывает дубликаты. Луис Рамальо предлагает метод, который не делает, но это немного неуклюже. Это немного короче:

$ irb
def invertWithDuplicates (оригинал)
  inverse = Hash.new() { |hash, key| хэш [ключ] = []; }
  original.each_pair() { | ключ, значение | обратное [значение].С (ключ); }
  обратный обратный
конец
h = { "4" => "счастье", "10" => "круто", "лала" => "54", "1" => "круто" }
invertWithDuplicates(ч)
=> {"счастье"=>["4"], "круто"=>["1", "10"], "54"=>["Лала"]}

Извините, что отошел от темы, предназначенной для OP, хотя я утверждаю, что это действительно соответствует заголовку поста "Обратный хэш в Ruby".

Если нужно:

hash = {:a => :x, :b => :y, :c => :y, :d => :z}

чтобы:

{:x => [:a], :y => [:b, c], :z => [:d] }

Можно:

h={};hash.to_a.each{|e|h[e[1]]||=[];h[e[1]]<<e[0]};h
Другие вопросы по тегам