Would this be the best way to sort a hash and return Hash object (instead of Array):
h = {"a"=>1, "c"=>3, "b"=>2, "d"=>4}
# => {"a"=>1, "c"=>3, "b"=>2, "d"=>4}
Hash[h.sort]
# => {"a"=>1, "b"=>2, "c"=>3, "d"=>4}
each
or each_pair
to iterate over it. Even then, I'd probably still grab the keys, sort those, then iterate over them grabbing the values as needed. That ensures the code will behave correctly on older Rubies.
In Ruby 2.1 it is simple:
h.sort.to_h
Note: Ruby >= 1.9.2 has an order-preserving hash: the order keys are inserted will be the order they are enumerated. The below applies to older versions or to backward-compatible code.
There is no concept of a sorted hash. So no, what you're doing isn't right.
If you want it sorted for display, return a string:
"{" + h.sort.map{|k,v| "#{k.inspect}=>#{v.inspect}"}.join(", ") + "}"
or, if you want the keys in order:
h.keys.sort
or, if you want to access the elements in order:
h.sort.map do |key,value|
# keys will arrive in order to this block, with their associated value.
end
but in summary, it makes no sense to talk about a sorted hash. From the docs, "The order in which you traverse a hash by either key or value may seem arbitrary, and will generally not be in the insertion order." So inserting keys in a specific order into the hash won't help.
I've always used sort_by
. You need to wrap the #sort_by
output with Hash[]
to make it output a hash, otherwise it outputs an array of arrays. Alternatively, to accomplish this you can run the #to_h
method on the array of tuples to convert them to a k=>v
structure (hash).
hsh ={"a" => 1000, "b" => 10, "c" => 200000}
Hash[hsh.sort_by{|k,v| v}] #or hsh.sort_by{|k,v| v}.to_h
There is a similar question in "How to sort a Ruby Hash by number value?".
sort_by
on a hash will return an array. You would need to map it out as a hash again. Hash[hsh.sort_by{|k,v| v}]
hsh.sort_by(&:last).to_h => {"b"=>10, "a"=>1000, "c"=>200000}
.
to_h
is only supported in Ruby 2.1.0+
sort_by{|k,v| v}.to_h)
Sort hash by key, return hash in Ruby
With destructuring and Hash#sort
hash.sort { |(ak, _), (bk, _)| ak <=> bk }.to_h
Enumerable#sort_by
hash.sort_by { |k, v| k }.to_h
Hash#sort with default behaviour
h = { "b" => 2, "c" => 1, "a" => 3 }
h.sort # e.g. ["a", 20] <=> ["b", 30]
hash.sort.to_h #=> { "a" => 3, "b" => 2, "c" => 1 }
Note: < Ruby 2.1
array = [["key", "value"]]
hash = Hash[array]
hash #=> {"key"=>"value"}
Note: > Ruby 2.1
[["key", "value"]].to_h #=> {"key"=>"value"}
v
you should prefix an underscore hash.sort_by { |k, _v| k }.to_h
You gave the best answer to yourself in the OP: Hash[h.sort]
If you crave for more possibilities, here is in-place modification of the original hash to make it sorted:
h.keys.sort.each { |k| h[k] = h.delete k }
No, it is not (Ruby 1.9.x)
require 'benchmark'
h = {"a"=>1, "c"=>3, "b"=>2, "d"=>4}
many = 100_000
Benchmark.bm do |b|
GC.start
b.report("hash sort") do
many.times do
Hash[h.sort]
end
end
GC.start
b.report("keys sort") do
many.times do
nh = {}
h.keys.sort.each do |k|
nh[k] = h[k]
end
end
end
end
user system total real
hash sort 0.400000 0.000000 0.400000 ( 0.405588)
keys sort 0.250000 0.010000 0.260000 ( 0.260303)
For big hashes difference will grow up to 10x and more
ActiveSupport::OrderedHash is another option if you don't want to use ruby 1.9.2 or roll your own workarounds.
You can use sort
method and then convert array back to hash with to_h
method
h = { "a" => 1, "c" => 3, "b" => 2, "d" => 4 }
h.sort.to_h
# => { "a" => 1, "b" => 2, "c" => 3, "d" => 4 }
@ordered = {}
@unordered.keys.sort.each do |key|
@ordered[key] = @unordered[key]
end
I had the same problem ( I had to sort my equipments by their name ) and i solved like this:
<% @equipments.sort.each do |name, quantity| %>
...
<% end %>
@equipments is a hash that I build on my model and return on my controller. If you call .sort it will sort the hash based on it's key value.
I borrowed Boris Stitnicky's inspired solution to patch an in-place sort!
method into Hash
:
def sort!
keys.sort!.each { |k| store k, delete(k) }
self
end
I liked the solution in the earlier post.
I made a mini-class, called it class AlphabeticalHash
. It also has a method called ap
, which accepts one argument, a Hash
, as input: ap variable
. Akin to pp (pp variable
)
But it will (try and) print in alphabetical list (its keys). Dunno if anyone else wants to use this, it's available as a gem, you can install it as such: gem install alphabetical_hash
For me, this is simple enough. If others need more functionality, let me know, I'll include it into the gem.
EDIT: Credit goes to Peter, who gave me the idea. :)
Success story sharing
h.sort{|a,z|a<=>z}.to_h
(tested 2.1.10, 2.3.3)a
is an array, not just the key). I've deleted my comment.h.map(&:sort).map(&:to_h)
.h.keys.sort.map { |k| [k, h[k]] }.to_h
. Not sure where is the difference on C level, but it's very clear on benchmark. Evensort_by(&:first)
is much faster.