How to sort not simple hash (hash of hashes)
I would change the data structure to an array of hashes:
my_array =
[
{:id => 78, :value=>64, :rating=>-155},
{:id => 84, :value=>90, :rating=>-220},
{:id => 95, :value=>39, :rating=>-92}
]
You can sort this kind of structure easily with
my_array.sort_by { |record| record[:rating] }
To get the hash-like functionality of fetching a record by id you can define a new method on my_array:
def my_array.find_by_id(id)
self.find { |hash| hash[:id] == id }
end
so after that you can do
my_array.find_by_id(id)
instead of
my_hash[id]
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", "..."=>"..."}}
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;
Note that the result of this code will give you an array:
$VAR1 = [
'foo2',
'foo1'
];
... not a nested array:
$VAR1 = [
['foo2', 'foo1']
];
Sorting a hash of hashes by the inner hash's values
I think it's very unlikely that you need the data in a hash structure like that. Certainly for the purposes of this task you would be better off with an array of arrays
use strict;
use warnings;
my @change;
while ( <DATA> ) {
push @change, [ split ];
}
print "@$_\n" for sort { $b->[2] <=> $a->[2] } @change;
__DATA__
gene1 condition1 10
gene2 condition1 0.5
gene3 condition1 1.5
gene1 condition2 2
gene2 condition2 13.5
gene3 condition2 0.25
output
gene2 condition2 13.5
gene1 condition1 10
gene1 condition2 2
gene3 condition1 1.5
gene2 condition1 0.5
gene3 condition2 0.25
If you explain what sort of access you need to the data then I am sure there is something better. For instance, I would suggest %gene
and %condition
hashes that mapped a gene or condition ID to a list of the array elements that used that gene. Then you could access the data when you know either the gene or the condition
How can I sort an array of hashes by the hashes name?
Try
@sorted = sort { (keys %$a)[0] cmp (keys %$b)[0] } @{$structure[$endpoint][1]};
This sorts the elements of the array (which are hash references) according to the first (only) key of each hash. If the keys are numeric use <=>
instead.
Test code:
%a = ( 'a' => 1 );
%b = ( 'zz' => 2 );
%c = ( 'g' => 3);
@arr = (\%a, \%b, \%c);
print "Unsorted\n";
for (@arr)
{
printf "%s\n",((keys %$_)[0]);
}
@sorted = sort { (keys %$a)[0] cmp (keys %$b)[0] } @arr;
print "\nSorted\n";
for (@sorted)
{
printf "%s\n",((keys %$_)[0]);
}
How can I sort a hash of hashes by key in Perl?
my @hash1s = sort {$a->{count} <=> $b->{count}} values %hash2;
Sorting a Hash of Array of Hashes by Internal Hash Value
Assuming $VAR
above is Data::Dumper::Dumper(\%matches)
...
If you don't want to make your data-structure nicer....
for my $gene ( sort keys %matches ) {
for my $hash ( sort {
my ($akey) = keys %$a;
my ($bkey) = keys %$b;
$a->{$akey} <=> $b->{$bkey}
} @{$matches{$gene}} ) {
my ($key) = keys %$hash;
print "$key => $hash->{$key}\n";
}
}
That sorts by the value (e.g: 12) not the key (e.g: 'p.I843_D846del'). I figured since you used a numeric comparison that you'd want to sort by the numeric value ;-)
edited: fixed body of inner loop.
edit 2:
I see you tried a Schwartzian Transform... if you keep your data-structure as is, that might be a more efficient solution... as follows:
for my $gene ( sort keys %matches ) {
print "$_->[0] => $_->[1]\n" for # print both key and value
sort { $a->[1] <=> $b->[1] } # sort by value (e.g: 35)
map { my ($key) = keys %$_; [$key, $_->{$key}] } # e.g:['p.D842_H845del' ,35]
@{$matches{$gene}};
}
But instead, I'd just fix the data structure.
Probably just make both the 'key' (e.g: 'p.I843_D846del') and the 'value' (e.g: 12) both values and give them consistent key names.
Is it possible to sort multidimensional hashes by child hash values?
You can convert it to an array of pairs, use sort_by
method to target the value you want to sort by, and then convert it back to a hash:
h = {
:a =>
{:order => 3},
:b =>
{:order => 1},
:c =>
{:order => 2}
}
h.sort_by {|k,v| v[:order]}.to_h
=> {:b=>{:order=>1}, :c=>{:order=>2}, :a=>{:order=>3}}
Sort both levels of keys for hash of hashes in perl
Perl makes it easy to efficiently sort the keys while iterating through a hash of hashes:
for my $cat (sort keys %HoH) {
# numerical sort:
for my $digits (sort { $a <=> $b } keys %{$HoH{$cat}}) {
print join("\t", $cat, $digits, $HoH{$cat}{$digits}) . "\n";
}
}
Ruby how to sort hash of hashes?
Ok, you didn't specify your question, so I'm assuming you want one layer removed. I changed the starting hash a bit to actually see if the sorting works:
my_hash = {
:category_1 => {
:solution_1 => { :order => 2 },
:solution_2 => { :order => 3 }
},
:category_2 => {
:solution_3 => { :order => 4 },
:solution_4 => { :order => 1 }
}
}
Hash[my_hash.inject({}) { |h, (k, v)| h.merge(v) }.sort_by { |k,v| v[:order] }]
#=> {:solution_4=>{:order=>1}, :solution_1=>{:order=>2}, :solution_2=>{:order=>3}, :solution_3=>{:order=>4}}
EDIT:
Taking into account your clarification (and still starting from the modified unsorted hash I posted above):
sorted = my_hash.inject({}) do |h, (k, v)|
h[k] = Hash[v.sort_by { |k1, v1| v1[:order] }]
h
end
#=> {:category_1=>{:solution_1=>{:order=>2}, :solution_2=>{:order=>3}}, :category_2=>{:solution_4=>{:order=>1}, :solution_3=>{:order=>4}}}
Related Topics
How to Use the Ruby "Self" Keyword
Bash: /Home/Xxx/.Rvm/Scripts/Rvm: No Such File or Directory
Carrierwave File Upload with Different File Types
Ruby Gems in Stand-Alone Ruby Scripts
Refactoring Activerecord Models with a Base Class Versus a Base Module
Use Separate Authentication Model with Devise on Rails
Ruby, Tor and Net::Http::Proxy
Begin Rescue Not Catching Error
Ruby Array to Hash: Each Element the Key and Derive Value from It
Using Will_Paginate Without :Total_Entries to Improve a Lengthy Query
How Rails Delegate Method Works
How to Test If All Items in an Array Are Identical
Trouble Downgrading Ruby on Os X Mavericks
How to Catch All Http Traffic (Local Proxy)