Impossible to sort a hash by key?


#1

Hi everyone!

is it impossible to sort a hash by key?

I have a hash like {“plums”=>3, “bananas”=>4, “apples”=>6}

And what i want is {“apples” => 6, “bananas” => 4, “plums” => 3}

Hash#sort returns an array. So i’ve tried this:

class Hash
def sort_by_key
array = self.sort
# the array is sorted!
array.inject({}) do |hash, value|
hash[value.first] = value.last
hash
end
end
end

But the result is {“plums”=>3, “bananas”=>4, “apples”=>6}!

So - is it impossible to sort a hash by key?

Oliver.


#2

Oliver K. wrote:

is it impossible to sort a hash by key?

Yes, because hashes are un-ordered containers.

Look up OrderedHash on the RAA for one possible solution.

Cheers,
Dave


#3

Hashes are by definition nonsortable. There is no guarantee that the
order you put them in will be the order you access them. The order
they appear in the hash probably has something to do with the hashed
value of each key (I think).


#4

On Jun 14, 2006, at 3:03 AM, Oliver K. wrote:

class Hash
But the result is {“plums”=>3, “bananas”=>4, “apples”=>6}!

So - is it impossible to sort a hash by key?

Oliver.


Posted via http://www.ruby-forum.com/.

A hash’s order is not guaranteed. If you want to display the hash in
sorted order you can do something like the following

% cat sorted_hash.rb
class Hash
def each_ordered_by_key
keys.sort.each do |key|
yield(key, self[key])
end
end
end

str = “{”

hash = {“plums”=>3, “bananas”=>4, “apples”=>6}

hash.each_ordered_by_key do |key, value|
str << "#{key.inspect}=>#{value.inspect}, "
end

str[-2, 2] = “}”

puts str

% ruby sorted_hash.rb
{“apples”=>6, “bananas”=>4, “plums”=>3}


#5

On 6/14/06, Oliver K. removed_email_address@domain.invalid wrote:

class Hash
But the result is {“plums”=>3, “bananas”=>4, “apples”=>6}!

So - is it impossible to sort a hash by key?

Oliver.


Posted via http://www.ruby-forum.com/.

Hashes are unordered, an “ordered hash” is an oxymoron.
If you need a hash to be ordered, the easiest way is to use an array.

irb(main):001:0> hsh = {:a => 1, :b => 2, :c => 3, :d => 4}
=> {:d=>4, :b=>2, :c=>3, :a=>1}
irb(main):002:0> hsh.sort_by { |k,v| k }
NoMethodError: undefined method <=>' for :d:Symbol from (irb):2:insort_by’
from (irb):2
from :0
irb(main):003:0> hsh.sort_by { |k,v| k.to_s }
=> [[:a, 1], [:b, 2], [:c, 3], [:d, 4]]


#6

Dave, Farrel, Logan - Thanks for your quick replys!
Hash#each_ordered_by_key is a good solution for my problem.

Oliver.


#7

h={:c=> 1, :b=> 2, :a => 3}
a=[]
h.keys.sort_by {|s| s.to_s}.each {|key| puts h[key] }
puts a

=> [[:a, 3], [:b, 2], [:c, 1]]

-------- Original-Nachricht --------
Datum: Wed, 14 Jun 2006 18:48:06 +0900
Von: Paul B. removed_email_address@domain.invalid
An: removed_email_address@domain.invalid
Betreff: Re: impossible to sort a hash by key?


#8

shorter:

h={:c=> 1, :b=> 2, :a => 3}
h.keys.sort_by {|s| s.to_s}.map {|key| [key, h[key]] }

-------- Original-Nachricht --------
Datum: Wed, 14 Jun 2006 18:48:06 +0900
Von: Paul B. removed_email_address@domain.invalid
An: removed_email_address@domain.invalid
Betreff: Re: impossible to sort a hash by key?


#9

On 14/06/06, Matthew H. removed_email_address@domain.invalid wrote:

Hashes are unordered, an “ordered hash” is an oxymoron.
If you need a hash to be ordered, the easiest way is to use an array.

=> [[:a, 1], [:b, 2], [:c, 3], [:d, 4]]

And you can then use assoc to look up the corresponding element:

a = [[:a, 1], [:b, 2], [:c, 3], [:d, 4]]
a.assoc(:c)[1] #=> 3

The look-up performance for large arrays will be far inferior to that
of a hash, however.

Paul.


#10

From: Peter E. [mailto:removed_email_address@domain.invalid]

shorter:

h={:c=> 1, :b=> 2, :a => 3}
h.keys.sort_by {|s| s.to_s}.map {|key| [key, h[key]] }

Shorter, faster, and (to my mind) clearer:

h.to_a.sort_by {|k,v| k.to_s }

If you’re not using symbols for your keys, you can just do:

h.to_a.sort

(I’m guessing that using OrderedHash will have the same problem of
symbols not being comparable in this situation).


#11

Daniel S. wrote:

h.to_a.sort_by {|k,v| k.to_s }

Or even:

h.sort_by {|k,v| k.to_s }


#12

Although an old topic, this example could be useful to someone:

h = ENV.to_hash
h.keys.sort.each{|k| puts k + " => " + h[k] + “
\n”}