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
Ruby Parenthesis Syntax Exception with I++ ++I
Map Array of Ints to Nested Array Access
How to Convert Character Code to What I Want
How to Transform the Utf8 Chars to Iso8859-1
How to Implement Editing/Formating Text Area in Rails
How to Access a Class Variable from the Outside in Ruby
How Does Sinatra Define and Invoke the Get Method
What Is the Ruby Equivalent of Preg_Quote()
Why Non-Explicit Splat Param Plus Default Param Is Wrong Syntax for Method Definition in Ruby 1.9
Incompatible Character Encoding in Rails - How to Just Fail/Skip Sensibly
Http.Post_Form in Ruby with Custom Headers
Errno::Enoent No Such File or Directory Rails 4
Rails Object Based Permission/Authorization Engine
How to Run Rails App in a Completely Isolated Instances of Chrome
Sort by Date in Descending Order in Ruby in Rails
Ruby 2.4 on MAC Os Mojave:Image Not Found ... Related to Openssl.Bundle
There Is a Way to Handle 'After_Save' and 'After_Destroy' "Equally"