Arrays ruby字母数字排序未按预期工作

Arrays ruby字母数字排序未按预期工作,arrays,ruby,sorting,alphanumeric,Arrays,Ruby,Sorting,Alphanumeric,给定以下数组: y = %w[A1 A2 B5 B12 A6 A8 B10 B3 B4 B8] => ["A1", "A2", "B5", "B12", "A6", "A8", "B10", "B3", "B4", "B8"] 预期的排序数组为: => ["A1", "A2", "A6", "A8", "B3", "B4", "B5", "B8", "B10", "B12"] 使用以下(普通)排序,我得到: irb(main):2557:0> y.sort{|a,b| pu

给定以下数组:

y = %w[A1 A2 B5 B12 A6 A8 B10 B3 B4 B8]
=> ["A1", "A2", "B5", "B12", "A6", "A8", "B10", "B3", "B4", "B8"]
预期的排序数组为:

=> ["A1", "A2", "A6", "A8", "B3", "B4", "B5", "B8", "B10", "B12"]
使用以下(普通)排序,我得到:

irb(main):2557:0> y.sort{|a,b| puts "%s <=> %s = %s\n" % [a, b, a <=> b]; a <=> b}
A1 <=> A8 = -1
A8 <=> B8 = -1
A2 <=> A8 = -1
B5 <=> A8 = 1
B4 <=> A8 = 1
B3 <=> A8 = 1
B10 <=> A8 = 1
B12 <=> A8 = 1
A6 <=> A8 = -1
A1 <=> A2 = -1
A2 <=> A6 = -1
B12 <=> B3 = -1
B3 <=> B8 = -1
B5 <=> B3 = 1
B4 <=> B3 = 1
B10 <=> B3 = -1  # this appears to be wrong, looks like 1 is being compared, not 10.
B12 <=> B10 = 1
B5 <=> B4 = 1
B4 <=> B8 = -1
B5 <=> B8 = -1
=> ["A1", "A2", "A6", "A8", "B10", "B12", "B3", "B4", "B5", "B8"]
irb(main):2557:0>y.sort{a,b |放置“%s%s=%s\n”%[a,b,ab];ab}
A1 A8=-1
A8 B8=-1
A2 A8=-1
B5 A8=1
B4 A8=1
B3 A8=1
B10 A8=1
B12 A8=1
A6 A8=-1
A1 A2=-1
A2 A6=-1
B12 B3=-1
B3 B8=-1
B5 B3=1
B4 B3=1
B10 B3=-1#这似乎是错误的,看起来比较的是1,而不是10。
B12 B10=1
B5 B4=1
B4 B8=-1
B5 B8=-1
=>[“A1”、“A2”、“A6”、“A8”、“B10”、“B12”、“B3”、“B4”、“B5”、“B8”]
…这显然不是我想要的。我知道我可以尝试先对alpha进行拆分,然后对数值进行排序,但似乎我不应该这么做

arr = ["A1", "A2", "B5", "B12", "A6", "AB12", "A8", "B10", "B3", "B4",
       "B8", "AB2"]
可能的大警告:我们现在一直在使用Ruby 1.8.7:(但即使是Ruby 2.0.0也在做同样的事情。我在这里遗漏了什么


建议?

需要自然或词典排序,而不是标准的基于字符值的排序。类似于这些gems的东西将是一个起点:


人类将像“A2”这样的字符串视为“a”,后跟数字2,并对字符串部分使用字符串排序,对数字部分使用数字排序。标准的
sort()
使用字符值排序,将字符串视为一个字符序列,而不管字符是什么。对于
sort()
“A10”“A2”看起来像['A','1','0']和['A','2'],因为“1”在“2”之前排序,而下面的字符不能改变顺序,“A10”因此在“A2”之前排序。对于人类来说,相同的字符串看起来像['A',10]和['A',2],在2之后进行10次排序,因此我们得到了相反的结果。可以对字符串进行操作,使基于字符值的
sort()
产生预期的结果,方法是将数字部分设置为固定宽度,并在左侧补零以避免嵌入空格,使“A2”变成“A02”,使用标准
sort()在“A10”之前进行排序

需要自然或词典排序,而不是标准的基于字符值的排序。类似于这些gems的东西将是一个起点:


人类将像“A2”这样的字符串视为“a”,后跟数字2,并对字符串部分使用字符串排序,对数字部分使用数字排序。标准的
sort()
使用字符值排序,将字符串视为一个字符序列,而不管字符是什么。对于
sort()
“A10”“A2”看起来像['A','1','0']和['A','2'],因为“1”在“2”之前排序,而下面的字符不能改变顺序,“A10”因此在“A2”之前排序。对于人类来说,相同的字符串看起来像['A',10]和['A',2],在2之后进行10次排序,因此我们得到了相反的结果。可以对字符串进行操作,使基于字符值的
sort()
产生预期的结果,方法是将数字部分设置为固定宽度,并在左侧补零以避免嵌入空格,使“A2”变成“A02”,使用标准
sort()在“A10”之前进行排序

您正在对字符串进行排序。字符串是像字符串一样排序的,而不是像数字一样排序的。如果您想像数字一样排序,那么您应该对数字而不是字符串进行排序。字符串
'B10'
在字典上比字符串
'B3'
小,这不是Ruby独有的,甚至不是编程独有的,不是吗这就是在编程、数据库、词典、词典、电话簿等各个领域,按字典顺序对文本进行排序的方式

您应该将字符串拆分为数字和非数字分量,并将数字分量转换为数字。数组排序是字典式的,因此最终的排序将完全正确:

y.sort_by {|s| # use `sort_by` for a keyed sort, not `sort`
  s.
    split(/(\d+)/). # split numeric parts from non-numeric
    map {|s| # the below parses numeric parts as decimals, ignores the rest
      begin Integer(s, 10); rescue ArgumentError; s end }}
#=> ["A1", "A2", "A6", "A8", "B3", "B4", "B5", "B8", "B10", "B12"]

您正在对字符串进行排序。字符串是像字符串一样排序的,而不是像数字一样排序的。如果您想像数字一样排序,那么您应该对数字而不是字符串进行排序。字符串
'B10'
在词典学上比字符串
'B3'
小,这不是Ruby独有的东西,甚至不是编程独有的东西,这就是为什么在编程、数据库、词典、词典、电话簿等领域,按Xicography排序一段文本几乎无处不在

您应该将字符串拆分为数字和非数字分量,并将数字分量转换为数字。数组排序是字典式的,因此最终的排序将完全正确:

y.sort_by {|s| # use `sort_by` for a keyed sort, not `sort`
  s.
    split(/(\d+)/). # split numeric parts from non-numeric
    map {|s| # the below parses numeric parts as decimals, ignores the rest
      begin Integer(s, 10); rescue ArgumentError; s end }}
#=> ["A1", "A2", "A6", "A8", "B3", "B4", "B5", "B8", "B10", "B12"]

如果您知道数字中的最大位数是多少,您也可以在比较期间在数字前面加上
0

y.sort_by { |string| string.gsub(/\d+/) { |digits| format('%02d', digits.to_i) } }
#=> ["A1", "A2", "A6", "A8", "B3", "B4", "B5", "B8", "B10", "B12"]
此处
'%02d'
指定以下内容,
%
表示值的格式,
0
然后指定以
0
s作为数字前缀,
2
指定数字的总长度,
d
指定您希望以小数(以10为基数)输出。您可以找到其他信息


这意味着
'A1'
将转换为
'A01'
'B8'
将变成
'B08'
'B12'
将保持
'B12'
,因为它已经有两个数字。这仅在比较期间使用。

如果您知道数字中的最大位数是多少,您也可以在数字前面加上前缀在比较过程中,rs与
0

y.sort_by { |string| string.gsub(/\d+/) { |digits| format('%02d', digits.to_i) } }
#=> ["A1", "A2", "A6", "A8", "B3", "B4", "B5", "B8", "B10", "B12"]
此处
'%02d'
指定以下内容,
%
表示值的格式,
0
然后指定以
0
s作为数字前缀,
2
指定数字的总长度,
d
指定您希望以小数(以10为基数)输出。您可以找到其他信息

这意味着
'A1'
将转换为
'A01'
'B8'
将变成
'B08'
'B12'
将保持
'B12'
max_len = arr.max_by(&:size).size
  #=> 4
arr.sort_by { |s| "%s%s%d" % [s[/\D+/], " "*(max_len-s.size), s[/\d+/].to_i] }
  #=> ["A1", "A2", "A6", "A8", "AB2", "AB12", "B3", "B4", "B5", "B8",
  #    "B10", "B12"]
arr.each { |s| puts "%s-> \"%s\"" %
  ["\"#{s}\"".ljust(7), s[/\D+/] + " "*(max_len-s.size) + s[/\d+/]] }

"A1"   -> "A  1"
"A2"   -> "A  2"
"B5"   -> "B  5"
"B12"  -> "B 12"
"A6"   -> "A  6"
"AB12" -> "AB12"
"A8"   -> "A  8"
"B10"  -> "B 10"
"B3"   -> "B  3"
"B4"   -> "B  4"
"B8,"  -> "B 8"
"AB2"  -> "AB 2"