Jake
Jake

Reputation: 1370

Rails apply a condition within a sort_by looking for value

I'm working on sorting a list of employees by their title with the following:

TAGS = {
'Region Manager' => 1,
'Region Sales Manager' => 2,
'General Manager' => 3,
'Residential Sales Manager' => 4,
'Commercial Sales Manager' => 5,
'Other' => 6
}.freeze

def sorting_by_title(employees)
 employees.sort_by do |x|
   TAGS[x[:title]]
 end
end

Works fine but...I also need to do an additional sort if an employee last name is Smith and needs to go before other individuals.

So I've tried doing something like:

return TAGS[x[:title]] unless x.last_name == "Smith"

Not working. It's erroring out on the show page with undefined method `each' for 2:Integer.

So my thought was I would build out another method to look for the last name.

def nepotism(employees)
 employees.group_by do |emp|
   case emp.last_name
   when /Smith/ then :moocher
   end
 end
end

So I tried then referencing it like:

return TAGS[x[:title]] unless x.nepotism
return TAGS[x[:title]] unless x.moocher

Neither of those work. Nepotism ends up with undefined method `nepotism' for # and Moocher ends up with the same. Then I realized a simple query would work a bit better:

def nepotism
 @nepotism = Employee.where(last_name: "Smith")
end

Is there a better way to sort_by a last_name if it matches Smith and THEN by the tags?

Upvotes: 1

Views: 942

Answers (2)

Björn Nilsson
Björn Nilsson

Reputation: 3773

Combine them like this

employees.sort_by do |x|
  x.last_name == "Smith" ? 0 : TAGS[x[:title]]
end

You can do it in the database as well (assuming Postgresql here)

def nepotism
  tagstring = "array_position(ARRAY"+TAGS.keys.to_s.gsub(/\"/,"'")+", last_name)"
  @nepotism = Employee.order("last_name = 'Smith' desc, " + tagstring)
end

Upvotes: 3

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230346

Here's a nice trick: in ruby you can compare arrays. And, consequently, use them as value in sort_by. They are compared element by element. If ary1[0] < ary2[0], then ary1 will be less than ary2, no matter the rest of the elements.

 employees.sort_by do |x|
   [
     x.last_name == "Smith" ? 0 : 1, # all zeroes come before all ones
     TAGS[x[:title]] # your main ordering parameter
   ]
 end

This would work very well, if there were many Smiths and you needed to sort them by titles between themselves. If there's only one Smith, then @Björn's solution is simpler.

Upvotes: 3

Related Questions