Given the following array:
y = %w[A1 A2 B5 B12 A6 A8 B10 B3 B4 B8]
=> [\"A1\", \"A2\", \"B5\", \"B12\", \"A6\", \"A8\", \"B10\", \"B3\", \"B4\", \"B8\"]
Here are a couple of ways to do that.
arr = ["A1", "A2", "B5", "B12", "A6", "AB12", "A8", "B10", "B3", "B4",
"B8", "AB2"]
Sort on a 2-element array
arr.sort_by { |s| [s[/\D+/], s[/\d+/].to_i] }
#=> ["A1", "A2", "A6", "A8", "AB2", "AB12", "B3", "B4", "B5", "B8",
# "B10", "B12"]
This is similar to @Jorg's solution except I've computed the two elements of the comparison array separately, rather than splitting the string into two parts and converting the latter to an integer.
Enumerable#sort_by compares each pair of elements of arr
with the spaceship method, <=>
. As the elements being compared are arrays, the method Array#<=> is used. See in particular the third paragraph of that doc.
sort_by
compares the following 2-element arrays:
arr.each { |s| puts "%s-> [%s, %d]" %
["\"#{s}\"".ljust(7), "\"#{s[/\D+/]}\"".ljust(4), s[/\d+/].to_i] }
"A1" -> ["A" , 1]
"A2" -> ["A" , 2]
"B5" -> ["B" , 5]
"B12" -> ["B" , 12]
"A6" -> ["A" , 6]
"AB12" -> ["AB", 12]
"A8" -> ["A" , 8]
"B10" -> ["B" , 10]
"B3" -> ["B" , 3]
"B4" -> ["B" , 4]
"B8" -> ["B" , 8]
"AB2" -> ["AB", 2]
Insert spaces between the alphameric and numeric parts of the string
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"]
Here sort_by
compares the following strings:
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"