为什么 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个问题。
- 为什么字母数字字符串排序会这样?
- 如何在不使用排序块的情况下实现所需的排序?
- 如何在 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。
我有一个数组 ["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个问题。
- 为什么字母数字字符串排序会这样?
- 如何在不使用排序块的情况下实现所需的排序?
- 如何在 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。