views:

38

answers:

3

I am building a list that will be sorted by the alphabet and am looking for a solution to grab the database result and sort it like this: photo

Any help is greatly appreciated!

A: 

Hmm, if you have a list that isn't too massive you might just do it naively like:

(This assumes you have a model Company which has a name attribute)

@grouped = {}
Company.all.each do |company|
  letter = company.name.slice(0,1).upcase
  @grouped[letter] ||= []
  @grouped[letter] << company
end

And now you can, in your view, do something such as:

<ul>
  <% @grouped.keys.sort.each do |letter| -%>
    <li>
      <h2><%= letter %></h2>
      <ul>
        <% @grouped[letter].each do |company| -%>
          <li><%= company.name %></li>
        <% end -%>
      </ul>
    </li>
  <% end -%>
</ul>

Update: If you want to extend the logic on what the 'letter' is, you would probably move the logic into the model, eg:

class Company
  # ... code

  def initial
    # find a number at the start of the string if it exists
    m = self.name.match(/^\d+/)
    return m[0] if m
    # or return the first letter upcased otherwise
    return self.name.slice( 0, 1 ).upcase
  end
end
thenduks
Thanks! ;) Got this to work. If the first "letter" is actually a number, how can I just return "123"?
Josh Brown
See my update. With the new method you can simply change the line `letter =` to `letter = company.initial`
thenduks
thenduks
A: 

Building on thenduks above, I like:

Company.rb

def initial
  return '?' if name.blank?
  # name.[0].upcase (updated to get the first character )
  name.slice(1).chr.upcase
end

view

<% # Company.all.group_by(&initial) do |initial, companies| (updated) %>

<% Company.all.group_by(&:initial).each do |initial, companies| %>
  <%= content_tag(:h2, initial)%>
  <% companies.each do |company|%>
    <%= link_to(company.name, company%>
  <% end %>
<% end %>
Jesse Wolgamott
You'd need to add a <ul></li> structure around <%= link_to(company.name, company%>, but you get the point
Jesse Wolgamott
Could not get this to work. I wanted to use this because it was "cleaner". Any suggestions?
Josh Brown
Josh -- if you're interested, I edited and tested it out. Several minor mistakes in my previous version.
Jesse Wolgamott
+1  A: 

Try this:

g = Company.all.group_by{|c|c.name.upcase[0..0]}
%w(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z).each do |letter|
  companies = g[letter] || []
  # process the name and the letter
end

This will work even in scenarios when you do not have items for few letters.

Note: You might want to handle the numbers and non English letters differently by modifying the group_by logic. You also have to change the letter name iteration list accordingly.

KandadaBoggu