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

Jac*_*ano 2 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?}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不适用于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>'
Run Code Online (Sandbox Code Playgroud)

同样的情况也是如此:

>> 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>'
Run Code Online (Sandbox Code Playgroud)

在教程的情况下,由于它是对状态双字母代码进行排序,因此可能的解决方案是:

state_data = state_data.sort_by{|state, counter| state.nil? ? "ZZ" : state }
Run Code Online (Sandbox Code Playgroud)

这显然是一个黑客.

什么是Ruby方式来处理这个?

Nik*_* B. 9

这条线

state_data.sort_by { |state, counter| state unless state.nil? }
Run Code Online (Sandbox Code Playgroud)

实际上是等同于简单state_data.sort,因为(state unless state.nil?) == state无论如何.你可以做的是从nil键中分离正确的键,只对前者进行排序:

state_data.select(&:first).sort + state_data.reject(&:first)
Run Code Online (Sandbox Code Playgroud)

在更一般的情况下,您还可以定义自定义比较函数,如下所示:

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) }
Run Code Online (Sandbox Code Playgroud)

通过快速查看,您会发现它非常丑陋.实际上,您应该在此重新考虑您对数据结构的选择.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
Run Code Online (Sandbox Code Playgroud)

你可以写得更干净,得到相同的结果:

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

作者还推荐了类似的代码

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

而Ruby的习语是:

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

这表明作者在Ruby中似乎不太合适.因此,我不能推荐该教程.