Сортуйте хеш за ключем, поверніть хеш у Ruby


257

Це був би найкращий спосіб сортувати хеш і повертати об’єкт Hash (замість масиву):

h = {"a"=>1, "c"=>3, "b"=>2, "d"=>4}
# => {"a"=>1, "c"=>3, "b"=>2, "d"=>4}

Hash[h.sort]
# => {"a"=>1, "b"=>2, "c"=>3, "d"=>4}

9
Я не впевнений, що в сортуванні хешу є велика перевага, якщо ви не використовуєте eachабо each_pairповторюєте його. Навіть тоді я б, мабуть, все-таки захоплював ключі, сортував їх, а потім перебирав їх, захоплюючи значення за потребою. Це гарантує, що код буде вести себе правильно на старих рубінах.
Олов'яний чоловік

Має сенс і в рубіні 1.9. У мене була колекція зустрічей, згрупована за датами (як ключі), що надходять від db, і я вручну сортував рубін. Напр. {"2012-09-22": [...], "2012-09-30": [...], "2012-10-12": [...]}
Adit Saxena

Так, я вважаю, що ваш процес Hash [h.sort] є більш ефективним, ніж сортування ключів, а потім знову звертаюся до хешу через відсортовані клавіші.
Дуглас


3
У вас було кілька років, щоб продумати своє рішення, чи готові ви прийняти відповідь? ;-)
Марк Томас

Відповіді:


219

У Ruby 2.1 це просто:

h.sort.to_h

@zachaysan, але це працює: h.sort{|a,z|a<=>z}.to_h(перевірено 2.1.10, 2.3.3)
whitehat101

@ whitehat101 Ви маєте рацію. У мене була помилка ( aце масив, а не лише ключ). Я видалив свій коментар.
зачаян

Тільки в разі , якщо хто - то шукає спосіб сортування масиву хеш, це буде робити трюк (де Н масив): h.map(&:sort).map(&:to_h).
Дж. М. Янцен

82

Примітка: у Ruby> = 1.9.2 є хеш, що зберігає замовлення: ключі замовлення вставляються буде в порядку їх перерахування. Нижче поширюється на старіші версії або на зворотний сумісний код.

Немає поняття відсортованого хешу. Так що ні, те, що ви робите, не правильно.

Якщо ви хочете, щоб це було відсортовано для відображення, поверніть рядок:

"{" + h.sort.map{|k,v| "#{k.inspect}=>#{v.inspect}"}.join(", ") + "}"

або, якщо вам потрібні ключі в порядку:

h.keys.sort

або, якщо ви хочете отримати доступ до елементів у порядку:

h.sort.map do |key,value|
  # keys will arrive in order to this block, with their associated value.
end

але підсумовуючи, немає сенсу говорити про відсортований хеш. У документах "Порядок, в якому ви переходите хеш або за ключем, або за значенням, може здатися довільним, і, як правило, не буде в порядку вставки". Тому вставлення ключів у певному порядку в хеш не допоможе.


Це вірно. Я б запропонував використовувати дорогоцінний камінь RBtree, щоб отримати впорядкований набір функціональності в рубіні.
Аарон Скруггс

26
Починаючи з 1.9.2 хеш-порядку вставки буде збережено. Дивіться redmine.ruby-lang.org/isissue/show/994
Девід

4
"Починаючи з 1.9.2 порядок хеш-вставок буде збережено.", І це солодко.
Олов'яний чоловік

2
Знову мій перший коментар (смішно): Наприклад, покладання на хеш-замовлення мовчки і непередбачувано порушиться для версій Ruby, старших за 1.9.2.
Жо Лісс

5
Як ця відповідь отримала близько 20 + 1, не відповідаючи ні на одну з двох частин питання ОП? "1) Чи буде це (приклад OP) найкращим способом сортування хешу, 2) та повернення об'єкта Hash"? Я не заздрю ​​+1: це лише після того, як прочитати відповідь, мені все ще залишаються оригінальні запитання. Крім того, якщо точка в тому , що не існує такої речі , як відсортованого хеш подивитися на коментарі для Вибирається відповіді на це питання stackoverflow.com/questions/489139 / ...
jj_

64

Я завжди користувався sort_by. Вам потрібно обернути #sort_byвихід, Hash[]щоб він вивів хеш, інакше він виводить масив масивів. Крім того, для цього ви можете запустити #to_hметод на масиві кортежів, щоб перетворити їх у k=>vструктуру (хеш).

hsh ={"a" => 1000, "b" => 10, "c" => 200000}
Hash[hsh.sort_by{|k,v| v}] #or hsh.sort_by{|k,v| v}.to_h

У " Як сортувати Ruby Hash за значенням числа " є аналогічне запитання . ".


8
використання sort_byна хеші поверне масив. Вам потрібно буде відобразити його знову як хеш. Hash[hsh.sort_by{|k,v| v}]
stevenspiel

1
так, клас перелічувача трактує хеші як масиви, я думаю
boulder_ruby

3
Правильно, сортування за значеннями: hsh.sort_by(&:last).to_h => {"b"=>10, "a"=>1000, "c"=>200000}.
Cary Swoveland

1
Зауважте, що дзвінки to_hпідтримуються лише в Ruby 2.1.0+
Phrogz

1
в коментарі є помилка друку, виправлення:sort_by{|k,v| v}.to_h)
тремтіння

13

Ні, це не так (Ruby 1.9.x)

require 'benchmark'

h = {"a"=>1, "c"=>3, "b"=>2, "d"=>4}
many = 100_000

Benchmark.bm do |b|
  GC.start

  b.report("hash sort") do
    many.times do
      Hash[h.sort]
    end
  end

  GC.start

  b.report("keys sort") do
    many.times do
      nh = {}
      h.keys.sort.each do |k|
        nh[k] = h[k]
      end
    end
  end
end

       user     system      total        real
hash sort  0.400000   0.000000   0.400000 (  0.405588)
keys sort  0.250000   0.010000   0.260000 (  0.260303)

Для великих хесів різниця виросте до 10 разів і більше


12

Ви дали найкращу відповідь собі в ОП: Hash[h.sort]Якщо ви бажаєте отримати більше можливостей, ось на місці модифікація оригінального хешу, щоб зробити його сортованим:

h.keys.sort.each { |k| h[k] = h.delete k }

1
Для допитливих це працює трохи швидше, ніж "сортування ключів" у stackoverflow.com/a/17331221/737303 на Ruby 1.9.3.
азот

9

Сортуйте хеш за ключем , поверніть хеш у Ruby

З руйнуванням і сортом Hash #

hash.sort { |(ak, _), (bk, _)| ak <=> bk }.to_h

Численні # sort_by

hash.sort_by { |k, v| k }.to_h

Хеш # сортування з поведінкою за замовчуванням

h = { "b" => 2, "c" => 1, "a" => 3  }
h.sort         # e.g. ["a", 20] <=> ["b", 30]
hash.sort.to_h #=> { "a" => 3, "b" => 2, "c" => 1 }

Примітка: <Рубін 2.1

array = [["key", "value"]] 
hash  = Hash[array]
hash #=> {"key"=>"value"}

Примітка:> Ruby 2.1

[["key", "value"]].to_h #=> {"key"=>"value"}

1
якщо vви не використовуєте, ви маєте префікс підкреслитиhash.sort_by { |k, _v| k }.to_h
silva96

6

ActiveSupport :: OrdersHash - це ще один варіант, якщо ви не хочете використовувати рубін 1.9.2 або прокручувати власні обхідні шляхи.


Посилання розірвано
Snake Sanders

3
Я зафіксував посилання, щоб вказати на Google, якщо якийсь історик захоче дослідити, як це можна було робити в минулому. Але кожен, хто зараз прийде на це, повинен використовувати новішу версію Ruby.
ереміт

0
@ordered = {}
@unordered.keys.sort.each do |key|
  @ordered[key] = @unordered[key]
end

4
Ось як би ви це зробили, якби у ruby ​​не було таких методів, як Hash # sort_by
boulder_ruby

0

У мене була така ж проблема (мені довелося сортувати обладнання за їх назвою), і я вирішив так:

<% @equipments.sort.each do |name, quantity| %>
...
<% end %>

@equipments - хеш, який я будую на своїй моделі і повертаю на свій контролер. Якщо ви зателефонуєте .sort, він буде сортувати хеш на основі його ключового значення.


-3

Мені сподобалось рішення в попередньому пості.

Я зробив міні-клас, назвав його class AlphabeticalHash. Вона також має метод , званий ap, який приймає один аргумент, Hash, в якості вхідних даних: ap variable. Акін до п.п. ( pp variable)

Але він (спробуйте і) надрукує в алфавітному списку (його клавіші). Не знаю, якщо хтось хоче скористатися цим, він доступний як дорогоцінний камінь, ви можете встановити його як такий:gem install alphabetical_hash

Для мене це досить просто. Якщо іншим потрібен додатковий функціонал, дайте мені знати, я включу його в дорогоцінний камінь.

EDIT: Кредит йде на Пітера , який дав мені ідею. :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.