Сортировка хэша с нулем в качестве ключа - PullRequest
2 голосов
/ 21 января 2012

Я прохожу этот урок: http://tutorials.jumpstartlab.com/projects/jsattend.html

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

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

К сожалению, это не работает на ruby ​​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?

1 Ответ

9 голосов
/ 21 января 2012

Линия

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 ключи не кажутся мне очень разумными.

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

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)

Хотя идиома Ruby будет такой:

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...