Обратный хеш в 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"}
Но это оставляет неприятный вкус (как и другие ответы, которые отлично работают, как этот). Если вам нужно сделать это, это может указывать на то, что хэш был не лучшим выбором.
Кроме того, вы можете использовать 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