Sort by Date in Descending Order in Ruby in Rails

Ruby/Rails sort_by date desc, name asc

A couple things. First, you should use sort_by in your example above, because the block takes one parameter as you used. The sort block takes two - item A and item B, for comparison. Next, you can get what you want by subtracting the date from something significant, like today's date:

objects.sort_by {|x| [Date.today - x.date, x.name]}

Even if dates might be in the future, the math will still work.

These pages detail the difference between sort and sort_by:

http://rubycuts.com/enum-sort

http://rubycuts.com/enum-sort-by

Btw, I agree with other commenters that you really should be ordering these records in your database query itself, but I wanted to solve the specific problem you asked about.

Rails sort date by upcoming first, then descending

upcoming, past = appointments.sort_by(&:start_date).partition{ |a| a.start_date.future? }
sorted = [*upcoming, *past.reverse]

PostgreSQL in Rails: sorting object by two date attributes in descending order

Your code will order first by renewal_date with nulls at the end, and then will look at the created_at if two records have the same renewal_date.

I assume that what you want to do is something like "max(renewal_date, created_at)", which will take the "last modification date", or another custom way to compare the two fields.
If then, you can find your answer here : merge and order two columns in the same model

Job.where(active: true).reorder('GREATEST(renewal_date, created_at) DESC')

Sorting array elements by date

I don't know why your expected output contains only 9 out of 12 entries, and that you mention an array of size 10, but I believe your want to sort all 12. My preference would be to do it the way @BroiSatse has, but I will offer two ways that do not use methods from the class Date. I include them because it has some interesting aspects for those new to Ruby.

dates = [
"6/23/2014", "8/5/2014", "8/19/2014", "6/26/2014", "8/19/2014", "10/19/2014",
"7/8/2014", "6/3/2014", "7/30/2014", "7/3/2014", "6/3/2014", "6/26/2014"
]

I have changed dates slightly so that it includes a date having a two-digit month ("10/19/2014").

Sort by arrays of integers

dates.sort_by { |d| d.split(?/).rotate(-1).map { |e| -e.to_i } }
#=> ["10/19/2014", "8/19/2014", "8/19/2014", "8/5/2014", "7/30/2014", "7/8/2014",
# "7/3/2014", "6/26/2014", "6/26/2014", "6/23/2014", "6/3/2014", "6/3/2014"]

Here's what's happening.

enum = dates.sort_by
#=> #<Enumerator: [
# "6/23/2014", "8/5/2014", "8/19/2014", "6/26/2014", "8/19/2014", "10/19/2014",
# "7/8/2014", "6/3/2014", "7/30/2014", "7/3/2014", "6/3/2014", "6/26/2014"
# ]:sort_by>

As you see, enum is an enumerator. The first value that it passes into its block is "6/23/2014", which it assigns to the block variable:

d = "6/23/2014"

a = d.split(?/)
#=> ["6", "23", "2014"]
b = a.rotate(-1)
#=> ["2014", "6", "23"]
c = b.map { |e| -e.to_i }
#=> [-2014, -6, -23]

Similarly, the third element of enum is converted to:

"8/19/2014".split(?/).rotate(-1).map { |e| -e.to_i }
#=> [-2014, -8, -19]

sort_by uses Array#<=> to compare pairs of elements. After reading that doc you'll understand why:

[-2014, -6, -23] <=> [-2014, -8, -19]
#=> 1

meaning that "8/19/2014" should precede "6/23/2014" in the sort.

Incidentally, it was necessary to convert months and days from strings to integers because those strings did not contain leading zeros for single-digit values. If we had left them as strings, "8" > "15", which is not what we want. Since we are converting to integers anyway, it was easier to make them negative than to leave them positive and apply reverse to the sorted array.

Sort by strings in year-month-day order padded with leading zeros as necessary, then reverse

dates.sort_by do |d|
d.sub(/(\d+)\/(\d+)\/(\d+)/) { "%4d%02d%02d" % [$3, $1, $2].map(&:to_i) }
end.reverse
#=> ["10/19/2014", "8/19/2014", "8/19/2014", "8/5/2014", "7/30/2014", "7/8/2014",
# "7/3/2014", "6/26/2014", "6/26/2014", "6/23/2014", "6/3/2014", "6/3/2014"]

Note that

arr = dates.map do |d|
d.sub(/(\d+)\/(\d+)\/(\d+)/) { "%4d%02d%02d" % [$3, $1, $2].map(&:to_i) }
end
#=> ["20140623", "20140805", "20140819", "20140626", "20140819", "20141019",
# "20140708", "20140603", "20140730", "20140703", "20140603", "20140626"]
arr.sort
#=> ["20140603", "20140603", "20140623", "20140626", "20140626", "20140703",
# "20140708", "20140730", "20140805", "20140819", "20140819", "20141019"]

See Kernel#sprintf for formatting directives. It appears that .map(&:to_i) is in fact unnecessary; that Ruby treates strings representing integers as integers when the d directive is used.

The regular expression /(\d+)\/(\d+)\/(\d+)/ matches one or digits (the month), which are saved to capture group 1, followed by '/', followed by one or digits (the day), which are saved to capture group 2, followed by '/', followed by one or digits (the year), which are saved to capture group 3. One could alternatively write this:

/(\d{1,2})\/(\d{1,2})\/(\d{4})/

or with named capture groups:

r = /(?<month>\d{1,2})\/(?<day>\d{1,2})\/(?<year>\d{4})/

dates.sort_by do |d|
d.sub(r) { "%4d%02d%02d" % [$~[:year], $~[:month], $~[:day]].map(&:to_i) }
end.reverse
#=> ["10/19/2014", "8/19/2014", "8/19/2014", "8/5/2014", "7/30/2014", "7/8/2014",
# "7/3/2014", "6/26/2014", "6/26/2014", "6/23/2014", "6/3/2014", "6/3/2014"]

$~ is the most recent MatchData object. It equals Regexp::last_match.

How to sort_by date in Ruby (not Rails)?

Something like this?

  class Array
def sort_by_date(direction="ASC")
if direction == "ASC"
self.sort
elsif direction == "DESC"
self.sort {|a,b| b <=> a}
else
raise "Invalid direction. Specify either ASC or DESC."
end
end
end

A multi-dimensional array is just an array of arrays, so call this method on the 'dimension' you want to sort.



Related Topics



Leave a reply



Submit