如何在ruby中对字母数字数组进行排序

我如何在ruby中以字母数字方式对数组数据进行排序?

假设我的数组是a = [test_0_1, test_0_2, test_0_3, test_0_4, test_0_5, test_0_6, test_0_7, test_0_8, test_0_9, test_1_0, test_1_1, test_1_2, test_1_3, test_1_4, test_1_5, test_1_6, test_1_7, test_1_8, test_1_9, test_1_10, test_1_11, test_1_12, test_1_13, test_1_14, ...........test_1_121...............]

我希望我的输出是:

 . . . test_1_121 . . . test_1_14 test_1_13 test_1_12 test_1_11 test_1_10 test_1_9 test_1_8 test_1_7 test_1_6 test_1_5 test_1_4 test_1_3 test_1_2 test_1_1 test_0_10 test_0_9 test_0_8 test_0_7 test_0_6 test_0_5 test_0_4 test_0_3 test_0_2 test_0_1 

您可以将块传递给sort函数以对其进行自定义排序。 在您的情况下,您将遇到问题,因为您的数字不是零填充,因此此方法零填充数字部分,然后对它们进行排序,从而产生所需的排序顺序。

 a.sort { |a,b| ap = a.split('_') a = ap[0] + "%05d" % ap[1] + "%05d" % ap[2] bp = b.split('_') b = bp[0] + "%05d" % bp[1] + "%05d" % bp[2] b <=> a } 

用于对在任意位置包含非填充序列号的字符串进行排序的通用算法。

 padding = 4 list.sort{|a,b| a,b = [a,b].map{|s| s.gsub(/\d+/){|m| "0"*(padding - m.size) + m } } a<=>b } 

其中padding是您希望数字在比较期间具有的字段长度。 如果字符串中找到的任何数字在比较之前将被填零,如果它包含少于“填充”数字的数字,则产生预期的排序顺序。

要产生user682932所要求的结果,只需在排序块之后添加.reverse ,它将自然排序(升序)翻转为降序。

通过字符串上的预循环,您当然可以动态地找到字符串列表中的最大位数,您可以使用它而不是硬编码某些任意填充长度,但这需要更多处理(更慢)和多一点代码。 例如

 padding = list.reduce(0){|max,s| x = s.scan(/\d+/).map{|m|m.size}.max (x||0) > max ? x : max } 

例如,如果您只是按字符串排序,则无法在“test_2”和“test_10”之间获得正确的顺序。 所以:

 sort_by{|s| s.scan(/\d+/).map{|s| s.to_i}}.reverse 

排序例程可以具有极大不同的处理时间。 基准测试的变化可以快速掌握最快的做法:

 #!/usr/bin/env ruby ary = %w[ test_0_1 test_0_2 test_0_3 test_0_4 test_0_5 test_0_6 test_0_7 test_0_8 test_0_9 test_1_0 test_1_1 test_1_2 test_1_3 test_1_4 test_1_5 test_1_6 test_1_7 test_1_8 test_1_9 test_1_10 test_1_11 test_1_12 test_1_13 test_1_14 test_1_121 ] require 'ap' ap ary.sort_by { |v| a,b,c = v.split(/_+/); [a, b.to_i, c.to_i] }.reverse 

它的输出:

 >> [ >> [ 0] "test_1_121", >> [ 1] "test_1_14", >> [ 2] "test_1_13", >> [ 3] "test_1_12", >> [ 4] "test_1_11", >> [ 5] "test_1_10", >> [ 6] "test_1_9", >> [ 7] "test_1_8", >> [ 8] "test_1_7", >> [ 9] "test_1_6", >> [10] "test_1_5", >> [11] "test_1_4", >> [12] "test_1_3", >> [13] "test_1_2", >> [14] "test_1_1", >> [15] "test_1_0", >> [16] "test_0_9", >> [17] "test_0_8", >> [18] "test_0_7", >> [19] "test_0_6", >> [20] "test_0_5", >> [21] "test_0_4", >> [22] "test_0_3", >> [23] "test_0_2", >> [24] "test_0_1" >> ] 

测试速度显示的算法:

 require 'benchmark' n = 50_000 Benchmark.bm(8) do |x| x.report('sort1') { n.times { ary.sort { |a,b| b <=> a } } } x.report('sort2') { n.times { ary.sort { |a,b| a <=> b }.reverse } } x.report('sort3') { n.times { ary.sort { |a,b| ap = a.split('_') a = ap[0] + "%05d" % ap[1] + "%05d" % ap[2] bp = b.split('_') b = bp[0] + "%05d" % bp[1] + "%05d" % bp[2] b <=> a } } } x.report('sort_by1') { n.times { ary.sort_by { |s| s } } } x.report('sort_by2') { n.times { ary.sort_by { |s| s }.reverse } } x.report('sort_by3') { n.times { ary.sort_by { |s| s.scan(/\d+/).map{ |s| s.to_i } }.reverse } } x.report('sort_by4') { n.times { ary.sort_by { |v| a = v.split(/_+/); [a[0], a[1].to_i, a[2].to_i] }.reverse } } x.report('sort_by5') { n.times { ary.sort_by { |v| a,b,c = v.split(/_+/); [a, b.to_i, c.to_i] }.reverse } } end >> user system total real >> sort1 0.900000 0.010000 0.910000 ( 0.919115) >> sort2 0.880000 0.000000 0.880000 ( 0.893920) >> sort3 43.840000 0.070000 43.910000 ( 45.970928) >> sort_by1 0.870000 0.010000 0.880000 ( 1.077598) >> sort_by2 0.820000 0.000000 0.820000 ( 0.858309) >> sort_by3 7.060000 0.020000 7.080000 ( 7.623183) >> sort_by4 6.800000 0.000000 6.800000 ( 6.827472) >> sort_by5 6.730000 0.000000 6.730000 ( 6.762403) >> 

Sort1和sort2以及sort_by1和sort_by2有助于为sortsort_by和具有reverse两者建立基线。

排序sort3和sort_by3是此页面上的另外两个答案。 Sort_by4和sort_by5是关于我如何做的两个旋转,sort_by5是我在几分钟的修补之后想出的最快的。

这表明算法中的微小差异如何能够在最终输出中产生差异。 如果有更多的迭代,或者更大的数组被排序,差异将更加极端。

与@ctcherry答案类似,但速度更快:

 a.sort_by {|s| "%s%05i%05i" % s.split('_') }.reverse 

编辑:我的测试:

 require 'benchmark' ary = [] 100_000.times { ary << "test_#{rand(1000)}_#{rand(1000)}" } ary.uniq!; puts "Size: #{ary.size}" Benchmark.bm(5) do |x| x.report("sort1") do ary.sort_by {|e| "%s%05i%05i" % e.split('_') }.reverse end x.report("sort2") do ary.sort { |a,b| ap = a.split('_') a = ap[0] + "%05d" % ap[1] + "%05d" % ap[2] bp = b.split('_') b = bp[0] + "%05d" % bp[1] + "%05d" % bp[2] b <=> a } end x.report("sort3") do ary.sort_by { |v| a, b, c = v.split(/_+/); [a, b.to_i, c.to_i] }.reverse end end 

输出:

 Size: 95166 user system total real sort1 3.401000 0.000000 3.401000 ( 3.394194) sort2 94.880000 0.624000 95.504000 ( 95.722475) sort3 3.494000 0.000000 3.494000 ( 3.501201) 

从它的外观来看,你想要使用sort函数和/或反向函数。

 ruby-1.9.2-p136 :009 > a = ["abc_1", "abc_11", "abc_2", "abc_3", "abc_22"] => ["abc_1", "abc_11", "abc_2", "abc_3", "abc_22"] ruby-1.9.2-p136 :010 > a.sort => ["abc_1", "abc_11", "abc_2", "abc_22", "abc_3"] ruby-1.9.2-p136 :011 > a.sort.reverse => ["abc_3", "abc_22", "abc_2", "abc_11", "abc_1"] 

好的,从您的输出,似乎您只是想让它反转,所以使用reverse()

 a.reverse