2012-01-21 3 views
2

Я иду через этот учебник: http://tutorials.jumpstartlab.com/projects/jsattend.htmlСортировка хэша с нулем в качестве ключевого

В итерации 7, шаг 3 мы получаем для сортировки хэша, называемого state_data, который имеет ноль в качестве ключа. Предлагаемое решение:

state_data = state_data.sort_by{|state, counter| state unless state.nil?} 

К сожалению, это не работает на рубин 1.9.2p290 (2011-07-09 пересмотра 32553) [x86_64-darwin11.0.0]. Например:

~% irb 
>> things = { nil => "a", 2 => "b", 3 => "c" } 
>> things.sort_by { |k, v| k unless k.nil? } 
ArgumentError: comparison of NilClass with 2 failed 
    from (irb):6:in `sort_by' 
    from (irb):6 
    from /Users/jacopo/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>' 

То же самое с эквивалентом:

>> things.sort_by { |k, v| k if k } 
ArgumentError: comparison of NilClass with 2 failed 
    from (irb):3:in `sort_by' 
    from (irb):3 
    from /Users/jacopo/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>' 

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

state_data = state_data.sort_by{|state, counter| state.nil? ? "ZZ" : state } 

который явно взломан.

Что такое Ruby-способ справиться с этим?

+0

Я думаю, что есть немного запаха кода, если я получил нуль как хэш-ключ. Я бы хотел исправить это. –

ответ

9

Линия

state_data.sort_by { |state, counter| state unless state.nil? } 

эффективно эквивалентна простой state_data.sort, потому что (state unless state.nil?) == state в любом случае. Что вы можете сделать, это отделить правильные ключи от nil ключей и только сортировать бывший:

state_data.select(&:first).sort + state_data.reject(&:first) 

В более общем случае, вы можете также определить пользовательскую функцию сравнения, как это:

def compare(a, b) 
    return a.object_id <=> b.object_id unless a || b 
    return -1 unless b 
    return 1 unless a 
    a <=> b 
end 

state_data.sort { |a, b| compare(a.first, b.first) } 

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

ОБНОВЛЕНИЕ: От взгляда на учебное пособие, к которому вы привязались, я полагаю, что в нем много субоптимальной информации. Взгляните на следующий кусок «Рубин», например:

ranks = state_data.sort_by{|state, counter| counter}.collect{|state, counter| state}.reverse 
state_data = state_data.sort_by{|state, counter| state} 

state_data.each do |state, counter| 
    puts "#{state}:\t#{counter}\t(#{ranks.index(state) + 1})" 
end 

Вы могли бы написать это гораздо более чисто и получить тот же результат:

rank = state_data.sort_by(&:last) 
state_data.sort.each do |data| 
    puts "%s:\t%d\t(%d)" % [*data, rank.index(data) + 1] 
end 

Автор рекомендует также код, как

filename = "output/thanks_#{lastname}_#{firstname}.html" 
output = File.new(filename, "w") 
output.write(custom_letter) 

Хотя идиома Руби будет:

filename = "output/thanks_#{lastname}_#{firstname}.html" 
File.open(filename, 'w') { |f| f.write(custom_letter) } 

Это показывает, что автор, похоже, не очень умен в Ruby. Таким образом, я не могу рекомендовать этот учебник.

+0

Спасибо! Информативно и полно. –

+0

@Jacopo: Мне просто показалось, что простого простого 'state.select (&: first) .sort + states.reject (&: first)' также может быть достаточно в вашем специальном случае. –

+1

* yikes *, that 'File.новый материал из учебника даже не плох Ruby, это плохо в * любом * языке с Unix-вдохновленной IO-библиотекой (C, C++, Java, Perl, Python, вы его называете), так как это утечка дескриптора файла. –

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