Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting Hash of Hashes by value (and return the hash, not an array)

I have the following hash:

user = {
  'user' => {
    'title' => {'weight' => 1, .... }
    'body' => {'weight' => 4, ....}
     ....
     ....
  }
}

Is is possible to get the User sorted by the weight key of its child hashes?

I looked in the Hash.sort, but it looks like it returns array rather than my original hash sorted.

like image 557
Dharam Gollapudi Avatar asked Jun 11 '09 20:06

Dharam Gollapudi


2 Answers

In Ruby 1.9, Hashes are sorted, but Hash#sort still returns an Array of Arrays. Imagine that! It does imply that you can build your own sorting method on top of it.

class Hash
  def sorted_hash(&block)
    self.class[sort(&block)]   # Hash[ [[key1, value1], [key2, value2]] ]
  end
end

Hashes are unsorted in Ruby 1.8. If you want Ruby 1.8 compatibility, you can use ActiveSupport's OrderedHash. It behaves like a 1.9-Hash, so you can define the same sorted_hash method on it:

class ActiveSupport::OrderedHash
  def sorted_hash(&block)
    self.class[sort(&block)]
  end
end

hash = ActiveSupport::OrderedHash.new
hash["b"] = "b"
hash["a"] = "a"
hash               #=> {"b"=>"b", "a"=>"a"}  => unsorted
hash.sorted_hash   #=> {"a"=>"a", "b"=>"b"}  => sorted!

You have to copy the sorted_hash method to your code, because it does not exist by default!

Update for deep sorting: If you're looking to sort on something else than the hash key, pass a block to the sorted_hash method as follows (assuming the implementation from above):

hash = ActiveSupport::OrderedHash.new
hash["a"] = { "attr" => "2", "..." => "..." }
hash["b"] = { "attr" => "1", "..." => "..." }

# Unsorted.
hash 
  #=> {"a"=>{"attr"=>"2", "..."=>"..."}, "b"=>{"attr"=>"1", "..."=>"..."}}

# Sort on the "attr" key. (Assuming every value is a Hash itself!)
hash.sorted_hash { |a, b| a[1]["attr"] <=> b[1]["attr"] }
  #=> {"b"=>{"attr"=>"1", "..."=>"..."}, "a"=>{"attr"=>"2", "..."=>"..."}}
like image 80
molf Avatar answered Oct 12 '22 22:10

molf


Hashes are fundamentally unsorted data structures; Hash#sort is, indeed, what you want. Either that, or sort a list of keys and then use that to enumerate when it's time to output the hash, instead of enumerating directly over the hash using its own methods.

like image 38
Jim Puls Avatar answered Oct 12 '22 22:10

Jim Puls