Sorting Hash of Hashes by value (and return the hash, not an array)
In Ruby 1.9, Hash
es are sorted, but Hash#sort
still returns an Array
of Array
s. 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
Hash
es 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", "..."=>"..."}}
Sort array of hashes by value of hash property in descending order preserving the initial order of hash
results = [
{ "rating"=>6, "id"=>10699 },
{ "rating"=>3, "id"=>19985 },
{ "rating"=>6, "id"=>1029 }
]
results.sort_by.with_index { |h,i| [-h["rating"], i] }
#=> [{"rating"=>6, "id"=>10699},
# {"rating"=>6, "id"=>1029}
# {"rating"=>3, "id"=>19985}]
See the third paragraph of the doc Array#<=> for an explanation of how Ruby orders arrays. When Ruby orders results[0]
and results[2]
she compares [-6, 0]
with [-6, 2]
. Because the first elements of these arrays are equal (-6
), Ruby compares 0
with 2
to break the tie, so results[0]
is ordered before results[2]
.Note that when the method Enumerable#sort_by has no block, it returns an enumerator. Here that enumerator is chained to Enumerator#with_index to produce another enumerator.
What's a clean way to sort a hash in Ruby without returning an array of key-value pair arrays?
Hashes aren't really sortable objects. Since Ruby 1.9, they maintain keys in the order in which they were added, which is convenient, but in terms of the data structure, order is not relevant.
You can test this by comparing { a: 1, b: 2 } == { b: 2, a: 1 } #=> true
. The same is not true for arrays, in which the order is an important feature.
You'll find many methods in Ruby actually convert hashes to enumerables, which are closer to arrays in that they have a defined order. When returning a value from something like sort
, you get an array.
You can easily convert it back into a hash using Hash[...]
:
Hash[hash.sort(...)]
How to sort a hash by the values of another array in Ruby?
You don't need to sort anything, and you also don't need to create a lookup table. Your array is already sorted the way you want and your sort_me
hash is already a lookup table:
elements = ['one', 'two', 'three']
# => ["one", "two", "three"]
sort_me = {'three' => 3, 'two' => 2, 'one' => 1}
# => {"three"=>3, "two"=>2, "one"=>1}
elements.map{|key| [key, sort_me[key] ] }.to_h
# => {"one"=>1, "two"=>2, "three"=>3}
If you want to use symbols and strings:elements = ['one', 'two', 'three']
# => ["one", "two", "three"]
sort_me = {three: 3, two: 2, one: 1}
# => {:three=>3, :two=>2, :one=>1}
elements.map{|key| [key.to_sym, sort_me[key.to_sym] ] }.to_h
# => {:one=>1, :two=>2, :three=>3}
Finally, if some elements
are not used in the hash, you could simply remove pairs with nil
values:elements = ['one', 'two', 'two_and_a_half', 'three']
# => ["one", "two", "two_and_a_half", "three"]
elements.map{|key| [key.to_sym, sort_me[key.to_sym] ] }.to_h
# => {:one=>1, :two=>2, :two_and_a_half=>nil, :three=>3}
elements.map{|key| [key.to_sym, sort_me[key.to_sym] ] }.reject{|k, v| v.nil? }.to_h
# => {:one=>1, :two=>2, :three=>3}
Sorting by hash values inside an array of hashes
First of all, sort_by
returns the sorted list, it doesn't sort it in place. That means that just:
@quarterbacks.sort_by { ... }
doesn't do anything useful as you're throwing away the sorted results. You'd need to add an assignment or use sort_by!
:@quarterbacks = @quarterbacks.sort_by { ... }
# or
@quarterbacks.sort_by! { ... }
Then you have understand how the sort_by
block works. sort_by
sorts using the block's return value, it is more or less like this:array.map { |e| [ sort_by_block_value[e], e ] }
.sort { |a, b| a.first <=> b.first }
.map { |e| e.last }
so your block needs to return something sensible rather than the nil
that puts
returns:@quarterbacks.sort_by! { |q| q[:dpp] }
How to sort an array of hashes in ruby
Simples:
array_of_hashes.sort_by { |hsh| hsh[:zip] }
Note:When using sort_by
you need to assign the result to a new variable: array_of_hashes = array_of_hashes.sort_by{}
otherwise you can use the "bang" method to modify in place: array_of_hashes.sort_by!{}
Sorting of array of hash
@final_array = @final_array.sort_by { |x, y| x[:ItemSize].downcase }
This makes sure that the case you pass into sort_by
is all the same. It does not change the case of the ItemSize values. How do I sort an array of hashes by a value in the hash?
Ruby's sort
doesn't sort in-place. (Do you have a Python background, perhaps?)
Ruby has sort!
for in-place sorting, but there's no in-place variant for sort_by
in Ruby 1.8. In practice, you can do:
sorted = sort_me.sort_by { |k| k["value"] }
puts sorted
As of Ruby 1.9+, .sort_by!
is available for in-place sorting:sort_me.sort_by! { |k| k["value"]}
Sorting Hash of Hashes by value
%HoH
is declared as a hash, but is defined as a hashreference. Use parentheses(...)
instead of braces{...}
.- You don't need to loop through the hash to sort it. Sort will take care of that.
- if you
sort {...} keys %HoH
, then the special variables$a
and$b
represent the keys of%HoH
as it performs the sort. $a
and$b
are in reverse order because your expected result is in decreasing order. (Update: Oh I just noticed that you had that in the first place.)The
zip
value in the nested hash is$HoH{$KEY}{'zip'}
, which is what you should sort by.use strict;
use warnings;
use Data::Dumper;
my %HoH = (
'foo1' => {
'bam' => 1,
'zip' => 0,
},
'foo2' => {
'bam' => 0,
'zip' => 1,
'boo' => 1
}
);
my @sorted = sort {$HoH{$b}{'zip'} <=> $HoH{$a}{'zip'}} keys %HoH;
print Dumper \@sorted;
$VAR1 = [
'foo2',
'foo1'
];
... not a nested array:$VAR1 = [
['foo2', 'foo1']
];
Sort array of hashes by a value
Here's my attempt. The worflow is:
1) Sort all inner arrays to get the biggest value (meaning most recent
numeric timestamp) to the first index.
2) With the biggest value in a known position (index 0) in the inner array, sort the outer arrays according the value of the the first
index in their inner array.
# Part 1
outer_list.map! do |h|
Hash[h.map do |k, v|
v = v.sort_by do |hsh|
hsh.first[1][0]['created_at'].to_i
end.reverse!
[k, v]
end]
end
# Part 2
sorted = outer_list.sort_by do |h|
h.first[1][0].first[1][0]['created_at'].to_i
end.reverse!
Related Topics
Private Messages with Faye and Rails
Importing CSV Data into a Ruby Array/Variable
What's an Example of Ruby Code That's "Too Clever"
Gem Install Rmagick Fails on Os X El Capitan
Typeerror: Can't Convert Net::Httpok into String
How to Find Word with The Greatest Number of Repeated Letters
How to Send a Keep-Alive Packet Through Websocket in Ruby on Rails
Save PDF File Shown by Pdfkit Middleware
Rails: What's Wrong with This Multiple Join with Conditions on The Associations
Understanding The Behaviour of Inject Used with a Lambda in Ruby
Activemodel::Validations on Anonymous Class
How to Run Capybara-Webkit (I.E. Forked Webkit_Server) on Heroku Cedar
How to Have Two Columns in One Table Point to The Same Column in Another with Activerecord
Shellwords.Shellescape Implementation for Ruby 1.8
Remove Adjacent Identical Elements in a Ruby Array
Outputting Stdout to a File and Back Again