【问题标题】:Why does ruby sort Alphanumeric strings with 1's and 0's as binary?为什么 ruby​​ 将 1 和 0 的字母数字字符串排序为二进制?
【发布时间】:2020-09-28 17:14:27
【问题描述】:

我有一个数组["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 中实现排序?

【问题讨论】:

  • 存在哪些标签模式/格式?
  • @stefan。它仅采用以下格式:字母然后数字,然后可能是也可能不是字母。
  • 您想分别对每个部分进行排序吗?即按字母顺序按前导字母,然后按数字按数字,然后再按字母顺序按尾随字母(如果存在)
  • 在我的情况下,我们可以忽略这封信。卡里已经详细回答了这个问题。它不仅解决了我的问题,还解决了我的模式可能出现的任何其他问题。请看一看。
  • 我看到了 Cary 的回答,很棒(和往常一样)。但是您特别要求解决方案“不使用排序块”“在 ActiveRecord 中排序”——您的要求有变化吗?

标签: ruby-on-rails ruby postgresql sorting natural-sort


【解决方案1】:

如果你的数组是

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_byString#[]

正则表达式/\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) 包含一个或多个非数字。

请参阅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) 包含一个或多个非数字。

【讨论】:

  • arr.sort_by { |s| s.split(/(\d+)/).each_slice(2).flat_map { |a, n| [a, n.to_i] } } 更笼统,虽然可能不太可读:)
【解决方案2】:

这应该对你有用,在排序之前将它们转换为数字。

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

这个奇怪的map(&amp;:to_i)map { |x| x.to_i }的简写

编辑:

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

Model.order("some_column::integer")

编辑二:

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

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

【讨论】:

    猜你喜欢
    • 2016-01-01
    • 1970-01-01
    • 2017-07-12
    • 1970-01-01
    • 2017-04-25
    • 2013-03-24
    • 1970-01-01
    • 2016-01-12
    • 1970-01-01
    相关资源
    最近更新 更多