Sorting Hash of Hashes by Value (And Return The Hash, Not an Array)

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

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", "..."=>"..."}}

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;

Note that the result of this code will give you an array:

$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



Leave a reply



Submit