I have got an array with those strings
["Goal A", "Goal B", "Goal C", "Target 1", "Target 10", "Target 11", "Target 12", "Target 13", "Target 2", "Target 20", "Target 21", "Target 3", "Target 4"]
Those values are already sorted alphabetically but as you can see, they are not sorted numerically. I would like to keep the alphabetic sorting but now I would like to sort them numerically. The result needs to be
["Goal A", "Goal B", "Goal C", "Target 1", "Target 2", "Target 3", "Target 4", "Target 10", "Target 11", "Target 12", "Target 13", "Target 20", "Target 21"]
I don't really know how to do that, any help welcome !!
CodePudding user response:
As Cary Swoveland, but just use \d
for numbers
arr = [
"Target 4", "Goal C", "Target 11", "Target 13", "Goal B", "Target 3",
"Target 1", "Target 2", "Target 12", "Target 20", "Target 21",
"Target 10", "Goal A"
]
arr.sort_by { |s| [s[/\d /].to_i, s] }
# => ["Goal A", "Goal B", "Goal C", "Target 1", "Target 2", "Target 3",
# "Target 4", "Target 10", "Target 11", "Target 12", "Target 13",
# "Target 20", "Target 21"]
CodePudding user response:
arr = [
"Target 4", "Goal C", "Target 11", "Target 13", "Goal B", "Target 3",
"Target 1", "Target 2", "Target 12", "Target 20", "Target 21",
"Target 10", "Goal A", "Dog D", "Cat 5"
]
arr.sort_by do |s|
t = s[/\S \z/]
Integer(t, exception: false) ? [1, t.to_i] : [0, t]
end
#=> ["Goal A", "Goal B", "Goal C", "Dog D", "Target 1", "Target 2",
# "Target 3", "Target 4", "Cat 5", "Target 10", "Target 11",
# "Target 12", "Target 13", "Target 20", "Target 21"]
My additions of "Dog D"
and "Cat 5"
were to illustrate my assumption of how the sort was to be performed; that is, on the end of the string only.
See Kernel#Integer.
Items are compared using Array#<=>. See especially the third paragraph of that doc. Thus, the first element of the array returned by Enumerable#sort_by ensures that in the sorted array strings ending in a string of digits appear after those that do not. The second element of the array returned by sort_by
breaks the ties.
The regular expression /\S \z/
is read, "Match one or more (
) non-whitespace characters (\S
) followed by the end of the string (\z
)". When s = "Target 12"
, for example, t = s[/\S \z/] #=> "12"
. See the fourth form of String#[] at that link.
For a different sorting assumption (which should be obvious for the example array, before and after it is sorted) we could write:
arr = [
"Target 4", "XGoal C", "ATarget 11", "Target 13", "Goal B", "Target 3",
"Target 1", "Target 2", "Target 12", "ATarget 20", "Target 21",
"ATarget 10", "XGoal A"
]
arr.sort_by do |s|
b, e = s.split
Integer(e, exception: false) ? [1, b, e.to_i] : [0, b, e]
end
#=> ["Goal B", "XGoal A", "XGoal C", "ATarget 10", "ATarget 11",
# "ATarget 20", "Target 1", "Target 2", "Target 3", "Target 4",
# "Target 12", "Target 13", "Target 21"]
CodePudding user response:
array.sort_by {|e| e.split(/(\d )/).map {|a| a =~ /\d / ? a.to_i : a }}