如何订购字母列表(“a”,“b”,“c”,...,“z”,“aa”,“ab”)? String#succ和< =>在这种情况下,似乎不能很好地融合在一起

时间:2011-01-04 22:28:16

标签: ruby-on-rails ruby string

我的一个对象('item')有一个ID('letter_id')格式为“a”,“b”,...,“aa”,“ab”等。要生成它我我在像这样的实例方法中使用ruby的String#succ

def set_letter_id
  last = parent.items.all(:order => "letter_id ASC").last
  if last.nil?
    self.letter_id = 'a'
  else
    self.letter_id = last.letter_id.succ
  end
end

现在这很有效,直到第28封信。第27个将正确生成“ aa ”,但last的值将始终返回带有' z '的letter_id的项目,因为返回的商品不遵循与String#succ相同的规则。

我是通过评论over here发现的 - 但现在我很难找到解决这个问题的好办法。问题基本上是这样的:

"aa".succ #=> "ab" - great, that's what I want.
"z"<=>"aa" #=> 1 - not so great, "z" should actually be less than "aa"

显然,这不一定是一个bug,但它使得以这种格式排序和排序letter_id列表非常困难。有没有人遇到这个并找到了解决方法,或者我可能会尝试的任何建议?谢谢!

3 个答案:

答案 0 :(得分:4)

您发布的链接的答案中有一个解决方案 - 您必须在<=>

的路上写自己的sort_by{|i|[i.length,i]}
irb> %w{a b c z aa ab zz aaa}.shuffle.sort_by { |i| [i.length,i] }
=> ["a", "b", "c", "z", "aa", "ab", "zz", "aaa"]

答案 1 :(得分:2)

您可以覆盖项目模型的<=>方法,首先按ID长度进行比较,然后按字母数字进行比较。

这样的事情:

class Item < ActiveRecord::Base
  # stuff

  def <=>(other)
    len_comp = self.letter_id.length <=> other.letter_id.length
    return len_comp if len_comp != 0
    self.letter_id <=> other.letter_id
  end
end

这样您首先要比较较短的ID长度(即"z"之前的"aa"),然后按字典顺序进行比较。

答案 2 :(得分:1)

这类问题正是为什么有些人不鼓励使用String#succ.RangeObject#to_a以及其他人发生冲突。

无论如何,你可能知道这一点,但这样的事情可能会有所帮助......

>> t
=> ["x", "y", "z", "aa", "ab", "ac", "ad", "ae", "af", "ag"]

>> t.shuffle.sort_by { |e| "%3s" % [e] }
=> ["x", "y", "z", "aa", "ab", "ac", "ad", "ae", "af", "ag"]

你甚至可以用这种方式重新规范化并省去sort_by