Home > Software design >  Sorting an array of strings alphabetically and then numerically in ruby
Sorting an array of strings alphabetically and then numerically in ruby

Time:08-25

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 }}
  •  Tags:  
  • ruby
  • Related