如何订购字母列表(“a”,“b”,“c”,…,“z”,“aa”,“ab”)? 字符串#sunk和在这种情况下似乎不能很好地协同工作

我的一个对象(’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相同的规则。

我从这里的评论中发现了这一点 – 但现在我很难找到解决这个问题的好方法。 问题基本上是这样的:

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

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

在你发布的链接的答案中有一个解决方案 – 你必须在sort_by{|i|[i.length,i]}编写自己的<=>

 irb> %w{abcz aa ab zz aaa}.shuffle.sort_by { |i| [i.length,i] } => ["a", "b", "c", "z", "aa", "ab", "zz", "aaa"] 

您可以覆盖Item模型的<=>方法,首先按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长度(即"aa"之前的"aa" "z" "aa" ),然后按字典顺序进行比较。

这种问题正是为什么有些人不鼓励使用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