2013-12-11 4 views
1

В Ruby, как мне поменять клавиши в хеше?Клавиши обмена в рубиновом хеше

Допустим, у меня есть следующий Hash:

{:one=>1, :two=>2, :three=>3, :four=>4 } 

То, что я хочу, чтобы преобразовать в:

{:one=>1, :three=>2, :two=>3, :four=>4} 

, который, своп клавиши: два и: три, но оставить их значения без изменений.

Что является наиболее эффективным решением для этого?

+1

Важно ли сохранить заказ? Если нет, просто замените значения. – Matzi

+1

Не могли бы вы объяснить причину запроса? –

+0

Важно соблюдать порядок.В реальной ситуации я получаю коллекцию (массив хэшей) в результате запуска хранимой процедуры mssql, которая затем представляется хелпером как html-таблица. Мне нужно изменить порядок нескольких столбцов. – Wastrox

ответ

4

Самый простой способ будет:

h = {:one => 1, :two => 2, :three => 3, :four => 4} 
h[:two], h[:three] = h[:three], h[:two] 

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

class Hash 
    def swap!(a, b) 
    self[a], self[b] = self[b], self[a] if key?(a) && key?(b) 
    self 
    end 

    def swap(a, b) 
    self.dup.swap!(a, b) 
    end 
end 

Примечание, однако, что оба этих решения сохранят порядок пар ключ-значение в хеше. Если вы действительно хотите поменять ключи, а также их значение, вы можете сделать это:

class Hash 
    def swap(a, b) 
    self.inject(Hash.new) do |h, (k,v)| 
     if k == a 
     h[b] = self[a] 
     elsif k == b 
     h[a] = self[b] 
     else 
     h[k] = v 
     end 
     h 
    end 
    end 
end 
{:one => 1, :two => 2, :three => 3, :four => 4}.swap(:two, :three) 
# results in {:one=>1, :three=>2, :two=>3, :four=>4} 

Хотя я не уверен, почему вы хотите, чтобы сделать это.

+1

+1 для первых двух строк. (Я думаю, вы могли бы пропустить все остальное). Кроме того, вы можете использовать [Enumerable # each_with_object] (http://ruby-doc.org/core-2.0.0/Enumerable.html#method-i-each_with_object), а не «вставлять» в ситуации, когда с помощью 'injection' вам нужно вернуть объект (здесь хэш) в итератор в конце блока. Это не обязательно для 'each_with_object'. –

+0

Узнай что-нибудь новое каждый день :) Я даже не знал о 'each_with_object', хотя я иногда расчесываю методы' Enumerable', чтобы напомнить себе. – migimunz

+0

Большое спасибо за полный ответ @migimunz, расширяя класс Hash с помощью swap! и методы подкачки сделали трюк в моей ситуации. – Wastrox

0

В хеше нет понятия порядка.

+4

Это правда, поскольку Ruby 1.9, Ruby поддерживает порядок вставки. Однако это не сортированный хэш. –

+0

@theTinMan Я не говорил о Ruby, а о семантике хэша. Верно, что реализация может иметь представление о порядке, однако это не то, на чем вы должны опираться. –

+2

На самом деле, мы гарантируем, что порядок вставки хеша Ruby будет сохранен. Это было обсуждено совсем немного, когда оно было впервые реализовано. Другие языки могут делать что-то по-другому, но мы говорим о Ruby здесь. –

1

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

hash = {:one=>1, :two=>2, :three=>3, :four=>4 } 
new_key_order = [:one, :three, :two, :four] 

new_hash = Hash[new_key_order.zip(hash.values)] 
# => {:one=>1, :three=>2, :two=>3, :four=>4} 

Это работает, потому что Рубин запоминает порядок вставки хешей, поэтому values всегда возвращает их в первоначальном порядке. Если вы хотите сделать это, не полагаясь на заказ вставки, это незначительное изменение:

old_key_order = [:one, :two, :three, :four] 
new_key_order = [:one, :three, :two, :four] 

new_hash = Hash[new_key_order.zip(hash.values_at(*old_key_order))] 
# => {:one=>1, :three=>2, :two=>3, :four=>4} 

Обратите внимание, что я выравниваются столбцы ключей, чтобы сделать то, что изменилось на самом деле выделиться. Это то, что мы делаем в нашей команде, чтобы помочь сделать это очевидным, когда что-то меняется в коде, который выглядит очень похожим.

Можно использовать параллельное назначение, но это очень быстро добавляет до грязного кода, когда вы имеете дело со множеством столбцов или полей. Легче определить порядок ввода и выходные порядки, как и выше, поэтому у вас есть очень визуальная ссылка на сопоставления, затем передайте их zip и дайте ему выполнить тяжелую работу, а затем принудительно верните ее в хэш.

Как в стороне, вот как я сделал бы это в Perl. Это использование отладчика:

perl -de 1 

    DB<1> %hash = ('one' => 1, 'two' => 2, 'three' => 3, 'four' => 4) 

    DB<2> x \%hash 
0 HASH(0x7fceb94afce8) 
    'four' => 4 
    'one' => 1 
    'three' => 3 
    'two' => 2 
    DB<3> @hash{'one', 'three', 'two', 'four'} = @hash{'one', 'two', 'three', 'four'} 

    DB<4> x \%hash 
0 HASH(0x7fceb94afce8) 
    'four' => 4 
    'one' => 1 
    'three' => 2 
    'two' => 3 

В принципе, Perl имеет возможность восстановить или назначать, с эквивалентом Руби values_at путем понуждения хэш в массив и определяющий порядок ключей. Это замечательно мощный инструмент в Perl, когда вы хотите реструктурировать множество данных.

Смежные вопросы