HiveBrain v1.2.0
Get Started
← Back to all entries
snippetrubyMinor

Sort array of hashes with nil values last

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
lastarraywithnilhashesvaluessort

Problem

I wrote a method which sorts an array of hashes by given hash keys. The method should put nil values at the end.

def sort(records, *attrs)
  records.sort do |a,b|
    result = 0
    attrs.each do |attr|
      unless a[attr] == b[attr]
        result = if a[attr].nil?
                   1
                 elsif b[attr].nil?
                   -1
                 else
                  a[attr]  b[attr]
                 end
        break
      end
    end
    result
  end
end

p sort([{:a => 1},{:a => nil},{:a => 2}], :a)
#=> [{:a=>1}, {:a=>2}, {:a=>nil}]
p sort([{:a => nil},{:a => 'x'},{:a => 'a'}], :a)
#=> [{:a=>"a"}, {:a=>"x"}, {:a=>nil}]


My solution looks quite complex. Is there a better way to achieve the ordering in Ruby?

Solution

Set Unions

Looking at your code, when you loop through the attrs, you are breaking after you find the first key that is in a and in b and in the passed attrs. This is also known as the union of the arrays. As such you can simplify the inner loop with:

(attrs & a.keys & b.keys).first


Shorthand if/elseif syntax

You can use the keyword then in conjunction with if and elsif to cut down on the whitespace of your if-eslif chain. The syntax would look like:

if attr.nil? then 0
elsif a[attr].nil? then 1
elsif b[attr].nil? then -1
else a[attr]  b[attr] end


Putting it all together

def sort(records, *attrs)
  records.sort do |a, b|
    attr = (attrs & a.keys & b.keys).first
    if attr.nil? then 0
    elsif a[attr].nil? then 1
    elsif b[attr].nil? then -1
    else a[attr]  b[attr] end
  end
end

Code Snippets

(attrs & a.keys & b.keys).first
if attr.nil? then 0
elsif a[attr].nil? then 1
elsif b[attr].nil? then -1
else a[attr] <=> b[attr] end
def sort(records, *attrs)
  records.sort do |a, b|
    attr = (attrs & a.keys & b.keys).first
    if attr.nil? then 0
    elsif a[attr].nil? then 1
    elsif b[attr].nil? then -1
    else a[attr] <=> b[attr] end
  end
end

Context

StackExchange Code Review Q#128089, answer score: 4

Revisions (0)

No revisions yet.