1

I'm having a minor problem with how RoR behaves when I tell it to display a result in a certain order.

I have a table called categories that contains a code column. Values in this code column include 1, 6, 12A, and 12B. When I tell the system to display the result (a dropdown) by order according to a foreign key id number and then a code value, it lists the codes in the order of 1, 12A, 12B, and 6 (which ideally should be 1, 6, 12A, and 12B).

Here is the dropdown:

collection_select(:category, :category_id, Category.order(:award_id, :code), :id, :award_code_category)

I know part of the problem is the A and B part of those two codes, I can't treat them as strict integers (code is a string type in the table).

I would love any thoughts about this.

3 Answers 3

1

steakchaser's answer called on:

['1', '12B', '12A', '6']

would return

['1', '6', '12B', '12A']

You lose the ordering of the letters.

You could create a helper to do the sorting:

def self.sort_by_category_codes(categories)
  sorted_categories = categories.sort do |cat1, cat2|
    if cat1.award_id == cat2.award_id
      # award id matches so compare by code
      if cat1.code.to_i == cat2.code.to_i
        # the leading numbers are the same (or no leading numbers)
        # so compare alphabetically
        cat1.code <=> cat2.code
      else
        # there was a leading number so sort numerically
        cat1.code.to_i <=> cat2.code.to_i
      end
    else
      # award ids were different so compare by them
      cat1.award_id <=> cat2.award_id
    end
  end

  return sorted_categories
end

Both ['1', '12A', '12B', '6'] and ['1', '12B', '12A', '6'] would return ['1', '6', '12A', '12B']

Then call:

collection_select(:category, :category_id, sort_by_category_codes(Category.all), :id, :award_code_category)

The only issue I see with my solution is that codes that start with letters such as just 'A' would be returned ahead of numbers. If you need 'A' to be returned after '1A' you'll need some extra logic in helper method.

Sign up to request clarification or add additional context in comments.

3 Comments

Would never have a situation where a letter would come first. Hmm, when I paste that code into the helper file and then use the dropdown code you provided, it says: undefined method `sort_by_category_codes' am I missing an obvious step? On Rails 4 I should have mentioned. Newbie with helpers.
I called it a "helper" method in a general sense. the "self." before the method name makes it a class method (static). So if that method was inside of a class called Helpers then you would call it like Helpers.sort_by_category_codes(Category.all) If you have a Category model, you could put the method in there and call Category.sort_by_category_codes(Category.all)
Thank you so much! I am so glad I asked, I feel like I am really learning so much from this site that the books I read go into only a minimal depth about. Here I learned something about "self" as a bonus to answering my question. The dropdown is ordered correctly now!
0

You could use a regex (most flexible depending on the pattern you really need to find) as part of the sorting to extract out the numeric portion:

['1', '12A', '12B', '6'].sort{|c1, c2| c1[/\d*(?:\.\d+)?/].to_i <=> c2[/\d*(?:\.\d+)?/].to_i}

Deleting non-integers when sorting is also a little easier to read:

['1', '12A', '12B', '6'].sort{|c1, c2| c1.delete("^0-9.").to_i <=> c2.delete("^0-9.").to_i}

Comments

0

To sort an array of those values you would:

["1", "6", "12A", "12B"].sort do |x, y|
    res = x.to_i <=> y.to_i
    res = x <=> y if res == 0
    res
end

To get the categories sorted in that order could do something like:

categories = Category.all.sort do |x, y|
    res = x.code.to_i <=> y.code.to_i
    res = x.code <=> y.code if res == 0
    res
end

From your code I inferred that you may want to sort on award_id with a second order sort on code. That would look like:

categories = Category.all.sort do |x, y|
    res = x.award_id <=> y.award_id
    res = x.code.to_i <=> y.code.to_i if res == 0
    res = x.code <=> y.code if res == 0
    res
end

2 Comments

I just tried that last version and for some reason it is not sorting first by the award_id. With that factor discounted it would work, it is sorting according to the code name properly.
Are you really wanting to sort by award_id or by some property of an Award object? The code I gave will sort by the award_id numeric value, not by some value of Award.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.