为什么 ruby 将带有 1 和 0 的字母数字字符串作为二进制进行排序?

Why does ruby sort Alphanumeric strings with 1's and 0's as binary?

我有一个数组 ["Q10", "Q100", "Q1000", "Q1000a", "Q1001", "Q98"]。排序后,我得到以下结果:

['Q100', 'Q1000', 'Q1000a','Q98', 'Q10', 'Q1001'].sort
["Q10", "Q100", "Q1000", "Q1000a", "Q1001", "Q98"]

由于这种行为,我无法正确排序 ActiveRecord 对象。我有一个 Question 的模型,它有一个字段 label。我需要根据标签对其进行排序。因此,带标签 Q1 的问题将首先出现,然后是带标签 Q1a 的问题,依此类推。我在上面的数组示例中描述的 ActiveRecord 中得到了类似的顺序。我正在使用 postgresql 作为我的数据库。

现在我有3个问题。

  1. 为什么字母数字字符串排序会这样?
  2. 如何在不使用排序块的情况下实现所需的排序?
  3. 如何在 ActiveRecord 中实现排序?

这应该可以解决问题,在排序之前将它们转换为数字。

['100', '1000', '98', '10', '1001'].map(&:to_i).sort

这个奇怪的map(&:to_i)是shorthand for map { |x| x.to_i }

编辑:

你可以用 AR 做到这一点。如果该列不包含伪装成字符串的数字,这将引发错误。

Model.order("some_column::integer")

编辑二:

如果它也包含字符串,试试这个。

Model.order("cast(some_column as integer))"

如果你的数组是

arr = ["Q10", "Q100", "Q1000", "Q8", "Q1001", "Q98"]

你可以写

arr.sort_by { |s| s[/\d+/].to_i }
  #=> ["Q8", "Q10", "Q98", "Q100", "Q1000", "Q1001"]

如果

s = "Q1000"

然后

s[/\d+/].to_i
  #=> 1000

参见 Enumerable#sort_by and String#[]

正则表达式 /\d+/ 匹配 s 的一个包含一位或多位数字的子串。


如果数组是

arr = ["Q10b", "Q100", "Q1000", "Q10a", "Q1001", "Q98", "Q10c"]

你可以写

arr.sort_by { |s| [s[/\d+/].to_i, s[/\D+\z/]] }
  #=> ["Q10a", "Q10b", "Q10c", "Q98", "Q100", "Q1000", "Q1001"] 

如果

s = "Q10b"

然后

[s[/\d+/].to_i, s[/\D+\z/]]
  #=> [10, "b"]

正则表达式 /\D+\z/ 匹配 s 的子字符串,该子字符串在字符串的末尾 (\z) 包含一个或多个 non-digits。

请参阅 Array#<=>,特别是第三段,了解排序时数组的排序方式。


如果数组是

arr = ["Q10b", "P100", "Q1000", "PQ10a", "Q1001", "Q98", "Q10c"]

你可以写

arr.sort_by { |s| [s[/\A\D+/], s[/\d+/].to_i, s[/\D+\z/]] }
  #=> ["P100", "PQ10a", "Q10b", "Q10c", "Q98", "Q1000", "Q1001"]

如果

s = "PQ10a"

然后

[s[/\A\D+/], s[/\d+/].to_i, s[/\D+\z/]]
  #=> ["PQ", 10, "a"]

正则表达式 /\A\D+/ 匹配 s 的子字符串,该子字符串在字符串的开头 (\A) 包含一个或多个 non-digits。