按字符和数字对数组进行排序

时间:2016-01-31 22:02:54

标签: ruby algorithm sorting numbers character

我正在尝试对这样的字符串数组进行排序:

['A1', 'A5', 'A13', 'B2', 'B5', 'B13']

当然,标准算法会按如下方式对其进行排序:

['A1', 'A13', 'A5', 'B13', 'B2', 'B5']

问题是,可能还有以下字符串:'B2K''B21A'

我如何让Ruby以人类逻辑的方式对这些字符串进行排序,就像第一个一样。我希望在不向字符串添加尾随零的情况下实现此目的,这将是显而易见的解决方案。

我想了解这是如何工作的。

1 个答案:

答案 0 :(得分:4)

本质上,您希望将字符串"A13"命名为元组["A", 13]。将"A13"转换为["A", 13]

很容易
str = "A13"
p [ str[0], str[1..-1].to_i ]
# => ["A", 13]

令人高兴的是,Ruby已经知道如何对这里生成的数组进行排序,所以你需要做的就是告诉它从每个字符串创建这样一个数组并在其排序比较中使用它。因为Ruby有Enumerable#sort_by

arr = ["B2", "A1", "A5", "B13", "A13", "B5"]

arr.sort_by do |str|
  [ str[0], str[1..-1].to_i ]
end
# => ["A1", "A5", "A13", "B2", "B5", "B13"]

此代码假定每个字符串都有一个字母后跟一个或多个数字。对于更复杂的字符串,例如"B21A",您可能希望使用正则表达式来提取每个部分,例如:

MATCH_PARTS_EXPR = /^([A-Z]+)(\d+)(.+)?/

此正则表达式假设每个字符串将包含一个或多个大写字母,后跟一个或多个数字,可选地后跟其他字符。我们可以像这样使用它,例如将"B21A"转为["B", 21, "A"]

str = "B21A"
letters, numbers, rest = str.match(MATCH_PARTS_EXPR).captures

p [ letters, numbers.to_i, *rest ]
# => ["B", 21, "A" ]

str2 = "A13"
letters, numbers, rest = str2.match(MATCH_PARTS_EXPR).captures

p [ letters, numbers.to_i, *rest ]
# => ["A", 13]

我们可以像以前一样使用sort_by

arr = ["B2", "B21A", "A1", "A5", "B13", "A13", "B5"]

arr.sort_by do |str|
  letters, numbers, rest = str.match(MATCH_PARTS_EXPR).captures
  [ letters, numbers.to_i, *rest ]
end
# => ["A1", "A5", "A13", "B2", "B5", "B13", "B21A"]