Home > front end >  Sort an array of hashes by value if such value is not nil (using sort_by)
Sort an array of hashes by value if such value is not nil (using sort_by)

Time:03-25

I am trying to perform a sort_by on a hash, but whenever I have a nil value I get: comparison of DateTime with nil failed

My goal is to perform a nil check (.present?) on x[:last_posted_at] inside the sort_by method. Is that possible? Example code:

posts = [
  { "name"=>"Alice", "last_posted_at"=> some_datetime },
  { "name"=>"Bob",   "last_posted_at"=> nil},
  { "name"=>"Clark", "last_posted_at"=> some_datetime - 1}
]

# expected result
posts.sort_by.{ |x| x[:last_posted_at] } #compare only if value is not nil
  #=> [{"name"=>"Alice", "last_posted_at"=> some_datetime},
  #    {"name"=>"Clark", "last_posted_at"=> some_datetime - 1},
  #    {"name"=>"Bob",   "last_posted_at"=> nil}]

I looked into the sort_by documentation and some of the posts here in stackoverflow, but I cannot find my answer. Any help or links are welcome! Thanks in advance!

CodePudding user response:

Use presence to return the value or nil, and || to return a default value if it is blank.

# Something sufficiently old to be older than any other time.
nil_time = Time.at(0)
posts.sort_by.{ |x|
  x[:last_posted_at].presence || nil_time
}

Note: DateTime is deprecated.

CodePudding user response:

I like Schwern's approach. But if there are more records without a date then another option might be to separate record without dates from the records with dates and only sort thoses with a date like this:

posts
  .partition { |v| v['last_posted_at'] }                   # separate by date presence
  .tap { |v| v.first.sort_by! { |v| v['last_posted_at']} } # only sort entries with a date
  .flatten                                                 # combine into one list again
  • Related