thedevlpr
thedevlpr

Reputation: 1101

Sort array of active records by multiple columns

Given an array of active records

jobs = [#<Job _id: 1, created_at: 2014-07-15 19:18:40 UTC, organization: "Acme In.c">,
        #<Job _id: 3, created_at: 2014-05-20 09:27:38 UTC, organization: "Baxter">, 
        #<Job _id: 2, created_at: 2014-11-25 12:21:00 UTC, organization: "Wizard">, 
        #<Job _id: 3, created_at: 2015-01-15 07:20:10 UTC, organization: "Baxter">]

How can I sort the array first by organization in ascending order A->Z and then by created_at descending to end up with this ...

jobs = [#<Job _id: 1, created_at: 2014-07-15 19:18:40 UTC, organization: "Acme In.c">,
        #<Job _id: 3, created_at: 2015-01-15 07:20:10 UTC, organization: "Baxter">,
        #<Job _id: 3, created_at: 2014-05-20 09:27:38 UTC, organization: "Baxter">,
        #<Job _id: 2, created_at: 2014-11-25 12:21:00 UTC, organization: "Wizard">]

I've tried to to do jobs.sort_by{|j| [j.organization, j.created_at]} but I'm not really sure how to specify multiple asc/desc rules and the closest I've gotten is sorted by organization but then sorting by created gives me oldest to newest instead of newest to oldest. I've also tried to group first and then sort but I couldn't get it to work either. Any help if appreciated.

Upvotes: 1

Views: 330

Answers (2)

joelparkerhenderson
joelparkerhenderson

Reputation: 35453

A good way is to use sort and <=> and nonzero? like this:

jobs.sort{|a,b| 
  (a.organization <=> b.organization).nonzero? ||
  (b.created_at <=> a.created_at)
}

This code says:

  1. Compare A and B by organization.
  2. If they differ, then we have our answer.
  3. If they are the same, then we need to do more.
  4. Compare B and A by time. (Note B & A are in reverse order)
  5. If they differ, then we have our answer.
  6. If they are the same, then the sort order doesn't matter. (Ruby sort is "unstable")

Example code independent of ActiveRecord:

require 'time'
require 'ostruct'

jobs = [
  OpenStruct.new(_id: 1, created_at: Time.parse("2014-07-15 19:18:40 UTC"), organization: "Acme Inc"),
  OpenStruct.new(_id: 3, created_at: Time.parse("2014-05-20 09:27:38 UTC"), organization: "Baxter"), 
  OpenStruct.new(_id: 2, created_at: Time.parse("2014-11-25 12:21:00 UTC"), organization: "Wizard"), 
  OpenStruct.new(_id: 3, created_at: Time.parse("2015-01-15 07:20:10 UTC"), organization: "Baxter")
]

Upvotes: 3

Arun Eapachen
Arun Eapachen

Reputation: 119

As mentioned in

http://guides.rubyonrails.org/active_record_querying.html

Try jobs.order("organization ASC", "created_at DESC") or the following

jobs.order("organization ASC").order("created_at DESC")

Upvotes: 0

Related Questions