sa0irxe
sa0irxe

Reputation: 29

Given array of hashes, sort alphabetically then by number

I have an array of hashes with id and name. The names can either have digits + strings or strings only. The goal is to sort alphabetically and if there are digits in the name, sort them by digit value.

Given:

array = [{id: "1", name: "Lorem 100"},
         {id: "2", name: "Lorem 101"}, 
         {id: "3", name: "Lorem 101-A"},   
         {id: "4", name: "Lorem 101-B"},  
         {id: "5", name: "Lorem 2"}, 
         {id: "6", name: "Ipsum (Lorems 55 & 55A)"},
         {id: "7", name: "Dolor"},
         {id: "8", name: "Sit"},
         {id: "9", name: "Amet"}]

Sort like this:

sorted_array = [ {id: "9", name: "Amet"},
                 {id: "7", name: "Dolor"},
                 {id: "6", name: "Ipsum (Lorems 55 & 55A)"},
                 {id: "5", name: "Lorem 2"}, 
                 {id: "1", name: "Lorem 100"},
                 {id: "2", name: "Lorem 101"}, 
                 {id: "3", name: "Lorem 101-A"},   
                 {id: "4", name: "Lorem 101-B"},  
                 {id: "8", name: "Sit"}]

What I've tried:

  1. array.sort_by { |hash| hash[:name] } -> did not sort like I wanted to i.e. Lorem 100 will be above Lorem 2
  2. array.sort_by { |hash| hash[:name][/\d+/].to_i } -> gives error because not all hashes have digits

Reaching out to this great community for any resource or suggestions that may help me solve this. Thank you!

Upvotes: 0

Views: 84

Answers (1)

Amadan
Amadan

Reputation: 198324

array.sort_by { |hash|
  hash[:name].split(/(\d+)/).map.with_index { |part, index|
    index.odd? ? part.to_i : part
  }
}

Split the name using consecutive digits as separator, using capture parentheses to keep separators (see String#split); then change the separators (i.e. every odd element) into integers, so they can compare numerically rather than lexicographically. Thus, sort_by will compare items such as ["Ipsum (Lorems ", 55, " & ", 55, "A)"]. Array#<=> does the intuitive thing, where ["Lorem ", 2] comes before ["Lorem ", 101].

Upvotes: 1

Related Questions