2016-05-24 15 views
2

У меня есть хэш:Сортировка Руби Hash по заказу в массиве ключей

sample = { bar: 200, foo: 100, baz: 100 } 

Как сортировать sample используя порядок ключей в sort_order:

sort_order = [:foo, :bar, :baz, :qux, :quux] 

Ожидаемый результат:

sample #=> { foo: 100, bar: 200, baz: 100 } 

Все, что я могу найти, это

new_hash = {} 
sort_order.each{|k| new_hash[k] = sample[k] unless sample[k].nil? } 
sample = new_hash 

Должен быть лучший способ. Советы?

Ключи без значения не должны присутствовать, то есть количество ключей остаются теми же, что не в случае с Sort Hash Keys based on order of same keys in array

+0

Что вам нужно лучше? Более короткие? Лучшая производительность? – Anand

+0

1. Короче 2. Лучшая производительность В таком порядке – absessive

+0

@Amit Как это отличается от моего решения? – absessive

ответ

4

Функциональный подход, использующий пересечение ключей:

new_sample = (sort_order & sample.keys).map { |k| [k, sample[k]] }.to_h 
#=> {:foo=>100, :bar=>200, :baz=>100} 

Как отмечалось @Stefan, абстракцию Hash#slice от ActiveSupport в значительной степени делает работу:

require 'active_support/core_ext/hash' 
new_sample = sample.slice(*sort_order) 
#=> {:foo=>100, :bar=>200, :baz=>100} 
+0

хорошая идея использовать 'Hash # slice' от AS. – Ilya

+2

'slice' проверяет, существуют ли ключи (похожие на [ответ Кита Беннетта] (http://stackoverflow.com/a/37405469/477037)), поэтому вы можете просто написать' sample.slice (* sort_order) ' – Stefan

+1

Stefan , круто! обновлено. – tokland

3

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

#!/usr/bin/env ruby 

def sorted_hash(input_hash, key_sort_order) 
    new_hash = {} 
    key_sort_order.each do |key| 
    if input_hash.has_key?(key) 
     new_hash[key] = input_hash[key] 
    end 
    end 
    new_hash 
end 

sort_order = [:foo, :bar, :baz, :qux, :quux] 
sample = { bar: 200, foo: 100, baz: 100 } 

puts sorted_hash(sample, sort_order) 
# Outputs: {:foo=>100, :bar=>200, :baz=>100} 

Упрощение заключается в использовании each_with_object:

def sorted_hash_two(input_hash, key_sort_order) 
    key_sort_order.each_with_object({}) do |key, result_hash| 
    if input_hash.has_key?(key) 
     result_hash[key] = input_hash[key] 
    end 
    end 
end 

puts sorted_hash_two(sample, sort_order) 
# Outputs: {:foo=>100, :bar=>200, :baz=>100} 

Мне нравится @ идея tokland о массиве пересечения (&) лучше, потому что elmiinates потребность в if состоянии:

def sorted_hash_ewo_intersection(input_hash, key_sort_order) 
    (key_sort_order & input_hash.keys).each_with_object({}) do |key, result_hash| 
    result_hash[key] = input_hash[key] 
    end 
end # produces: {:foo=>100, :bar=>200, :baz=>100} 
+0

'' 'each_with_object''' выглядит проще, однако я не вижу, как это лучше, чем мое решение. – absessive

+0

Как я упоминаю в ответе, я думаю, что важно использовать 'has_key? ', А не тест для nil. В стороне, я рекомендую выразить это как метод; который отделяет рассматриваемую логику от входов и выходов; и ваша фамилия 'sample = new_hash' не нужна. –

+0

Я просто добавил подход @ tokland к использованию пересечения массива (используя '&'), но используя 'each_with_object', и, как этот, лучший. –

3

Пожалуйста, см. Мой this answer:

sort_order = [:foo, :bar, :baz, :qux, :quux, :corge, :grault, 
       :garply, :waldo, :fred, :plugh, :xyzzy, :thud] 
sample = { bar: 200, foo: 100, baz: 100 } 

sample.sort_by {|k, _| sort_order.index(k)}.to_h 
=> {:foo=>100, :bar=>200, :baz=>100} 
+0

Не проблема для небольших входов, но обратите внимание, что это квадратично. – tokland

0

Вот еще один способ это может быть сделано:

(sort_order & sample.keys).zip([nil]).to_h.merge(sample) 
#=> {:foo=>100, :bar=>200, :baz=>100} 

Объяснение:

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

(sort_order & sample.keys).zip([nil]).to_h 
#=> {:foo=>nil, :bar=>nil, :baz=>nil} 

И потом, мы объединяем этот хэш с sample, чтобы получить значения из sample.

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