在Ruby中用数字和字符对字符串数组进行排序

时间:2018-11-27 23:23:29

标签: ruby sorting

我有一个包含给定字符串的数组

array = [
  "1mo-30-super",
  "1mo-40-classic",
  "1mo-30-classic",
  "1mo-110-super",
  "1mo-20-extra",
  "6mo-21-super",
  "6mo-11-super",
  "12mo-21-classic",
  "12mo-21-super"
]

如何对数组进行排序,使其按数字顺序,然后按字母顺序排列,以便数组显示如下:

array = [
  "1mo-20-extra",
  "1mo-30-classic",
  "1mo-30-super",
  "1mo-40-classic",
  "1mo-110-super",
  "6mo-11-super",
  "6mo-21-super",
  "12mo-21-classic",
  "12mo-21-super"
]

3 个答案:

答案 0 :(得分:1)

您正在寻找一种“自然”的排序方式,其中将数字子字符串与数字进行比较,而将非数字部分与字符串进行比较。足够方便的是,Ruby中的数组会逐个元素地进行比较,并且您的格式相当规则,因此您可以通过#sort_by调用和一些麻烦的操作将"12mo-21-classic"转换为[12, 'mo-', 21, '-classic']。例如:

# This is a bit complicated so we'll give the logic a name.
natural_parts = ->(s) { s.match(/(\d+)(\D+)(\d+)(\D+)/).to_a.drop(1).map.with_index { |e, i| i.even?? e.to_i : e } }
array.sort_by(&natural_parts)

答案 1 :(得分:1)

array.sort_by { |s| [s.to_i, s[/(?<=-)\d+/].to_i, s.gsub(/\A.+-/,'')] }
  #=> ["1mo-20-extra", "1mo-30-classic", "1mo-30-super", "1mo-40-classic", "1mo-110-super",
  #    "6mo-11-super", "6mo-21-super", "12mo-21-classic", "12mo-21-super"]

对数组进行排序时,方法Arrays#<=>用于对数组进行排序。有关如何执行此操作的说明,请参见文档的第三段。

用于排序的数组如下。

array.each do |s|
  puts "%-15s -> [%2d, %3d, %s]" % [s, s.to_i, s[/(?<=-)\d+/].to_i, s.gsub(/\A.+-/,'')]
end

1mo-30-super    -> [ 1,  30, super]
1mo-40-classic  -> [ 1,  40, classic]
1mo-30-classic  -> [ 1,  30, classic]
1mo-110-super   -> [ 1, 110, super]
1mo-20-extra    -> [ 1,  20, extra]
6mo-21-super    -> [ 6,  21, super]
6mo-11-super    -> [ 6,  11, super]
12mo-21-classic -> [12,  21, classic]
12mo-21-super   -> [12,  21, super]

(?<=-)正向查找。它要求匹配必须紧跟连字符。 /\A.+-/匹配字符串的开头,后跟一个或多个字符,后跟一个连字符。由于正则表达式默认为 greedy ,因此它会在第二个连字符处得出匹配结果。

请注意,没有必要使用正则表达式:

array.sort_by { |s| [s.to_i, s[s.index('-')+1..-1].to_i, s[s.rindex('-')+1..-1]] }

答案 2 :(得分:0)

您可以链接多个#sort方法调用,每个方法调用均按字符串的不同部分排序(从优先级最低的字符串开始):

array.sort { |a,b| a.match(/-(.*)$/)[1] <=> b.match(/-(.*)-/)[1] } # sort by last element ('classic', 'super')
     .sort { |a,b| a.match(/-(\d+)-/)[1].to_i <=> b.match(/-(\d+)-/)[1].to_i } # sort by the number between dashes 
     .sort { |a,b| a.to_i <=> b.to_i  } # sort by the initial number

=> ["1mo-20-extra",
 "1mo-30-classic",
 "1mo-30-super",
 "1mo-40-classic",
 "1mo-110-super",
 "6mo-11-super",
 "6mo-21-super",
 "12mo-21-super",
 "12mo-21-classic"]