Sort by time?

Tonight I tried to run this code:

    broadcast.transcriptions
      .sort! { |transcription| transcription.created_at.to_unix }
      .last

broadcast and transcription are Avram model instances, and created_at is a Time.

I was at first surprised that crystal’s Array#sort doesn’t know how to compare Time, but I jumped to #to_unix thinking that’d be an easy escape. Unfortunately, this cryptic reaper (still) popped out of the compiler:

Error target worker failed to compile:
In /Users/robert/.asdf/installs/crystal/1.11.2/src/array.cr:1661:10

1661 | {% raise "Expected block to return Int32 or Nil, not #{U}" %}
          ^----
Error: Expected block to return Int32 or Nil, not Int64

Brutal! Both a dead end, and a cryptic error message :skull_and_crossbones:

Is there a way to trick the sorting algorithm to sort by something like Time, which is quite sortable if you know what you’re doing?

You probably want to use #sort_by!, otherwise the block version of #sort! expects you to return 0, 1, -1, or nil as per semantics of <=>. There’s also no reason to call #to_unix as Time values include Comparable inherently.

Also might be more efficient to just do the sort directly in the SQL with a LIMIT 1 if that would be possible. Otherwise you could prob get away with just calling #max or #min vs having to sort at all. This would have to be #max_by like @straight-shoota pointed out since it’s not a collection of Time directly.

1 Like

I created a little PR to improve the error: Better error reporting for `sort` by beta-ziliani · Pull Request #14693 · crystal-lang/crystal · GitHub

2 Likes

#sort_by is indeed what I needed, thank you!

If you want the last (i.e. maximum) value, wouldn’t transcriptions.max_by(&.created_at) be sufficient?

1 Like