使用nil作为键对哈希进行排序

时间:2012-01-21 15:42:40

标签: ruby null

我正在阅读本教程: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 个答案:

答案 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键对我来说似乎不太合理。

更新:通过查看您链接的教程,我认为其中有很多次优信息。看一下下面的“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中似乎不太合适。因此,我不推荐该教程。