Natural sorting in a text field in Rails

835 views Asked by At

I a have list of photo albums as below:

"Day 10 - Kblah"
"Day 9 - Lblah"
"Day 8 - Sblah"
"Day 7 - Ublah"
"Day 6 - Sblah"
"Day 5 - Cblah"
"Day 4 - Gblah"
"Day 3 - Sblah"
"Day 2 - Dblah"
"Day 1 - Hblah"
"Another album"
"Some more albums"

When I sort them by their title I end up with:

"Another album"
"Day 1 - Hblah"
"Day 10 - Kblah"
"Day 2 - Dblah"
"Day 3 - Sblah"
"Day 4 - Gblah"
"Day 5 - Cblah"
"Day 6 - Sblah"
"Day 7 - Ublah"
"Day 8 - Sblah"
"Day 9 - Lblah"
"Some more albums"

Where as I would like them to be:

"Another album"
"Day 1 - Hblah"
"Day 2 - Dblah"
"Day 3 - Sblah"
"Day 4 - Gblah"
"Day 5 - Cblah"
"Day 6 - Sblah"
"Day 7 - Ublah"
"Day 8 - Sblah"
"Day 9 - Lblah"
"Day 10 - Kblah"
"Some more albums"

I basically want to Natural sort on the title but an alphabetical natural sort. Is there any gem of any type that will do this correctly for me?

Kind regards, Neil

3

There are 3 answers

0
rctneil On BEST ANSWER

Thanks for the replies. I managed to locate a gem which worked wonders. It's called NaturalSorter and I just called it on my @albums instance variable in my controller to sort them by title and then it was passed through to the view.

Kind regards, Neil

0
iain On

I know of no gem that will figure this out automatically. I'd imagine that solving this problem generically is rather difficult.

If the problem however isn't generic however, it might be doable.

It starts with passing a block to the sort:

array.sort do |a, b|
  # return -1, 0 or 1 in this block, usually done via the <=> operator
end

You might want to leave of the "Day" part:

DAY_REGEX = /^Day (\d+) -/

array.sort do |a, b|
  if is_day?(a) && is_day(b)
    value_of_day(a) <=> value_of_day(b)
  else
    a <=> b
  end
end

def is_day?(str)
  str =~ DAY_REGEX
end

def value_of_day(str)
  DAY_REGEX.match(str)[1].to_i
end

I know, it is hacky and ugly. But I hope it inspires you on how to solve this on your own, if you can't find a proper solution.

Edit: maybe change the technique to read something like: contains_numbers. Write some tests to do things like: when the part of the string until the first name is the same in both strings, then remove those, convert the numbers, and sort on that.

0
Pavling On

How are you generating your list of photo album names? Can you go back to that step and figure the sorting there? (for instance, are they being drawn from a DB table of user albums?). Or replace the array with a hash of album names indexed by something sort-able, and then sort the keys and retrieve the values from the hash in that order.