Ruby Hash - sort by value and print keys - ruby

This is my hash.
=> {"f11"=>1, "f12"=>3, "f13"=>3, "f07"=>5, "f10"=>1}
I'd like to sort by the values largest to smallest and then make an array out of the keys.
=> ["f07", "f12", "f13", "f11", "f10"]

Here's a one-liner for you (I love ruby!):
h.keys.sort {|a, b| h[b] <=> h[a]}
Hope that helps!

Hash has the Enumerable module mixed in which provides us with methods like sort and sort_by.In this situation we can use sort_by to get a collection by order of values.
h={"f11"=>1, "f12"=>3, "f13"=>3, "f07"=>5, "f10"=>1}
h.sort_by{ |key, value| -value }
=> [["f07", 5], ["f12", 3], ["f13", 3], ["f11", 1], ["f10", 1]]

Even shorter!:
h.keys.sort_by{|a| h[a]}.reverse

a = {"f11"=>1, "f12"=>3, "f13"=>3, "f07"=>5, "f10"=>1}
b = Hash[a.sort_by{|k,v| v}]
puts b.keys.reverse

Related

Ruby hash to array convert

I have something like this:
[#TrajectoryMeasurement depth: 0, move_e: 234>,
#TrajectoryMeasurement depth: 1475, move_e: 123>]
How to convert it to:
[[0, 234], [1475,123]]
If it's an array of objects as I suspect you can use the #collect method on Array:
array = [#TrajectoryMeasurement depth: 0, move_e: 234>,
#TrajectoryMeasurement depth: 1475, move_e: 123>]
array.collect { |x| [x.depth, x.move_e] }
# => [[0, 234], [1475, 123]]
Supposing you really had a hash, all you need is calling .to_a to get exactly what you asked for.
{a:1, b:1}.to_a
=> [[:a, 1], [:b, 1]]
Alas, as it was said before, it wouldn't see, what you have there is a Hash, unless that's an ad-hoc representation of it.
Depending on the structure of your hash you might also want to have a look at .flatten.

Using Hash.new as an initial value when reducing an array

I have an array like so
[1,1,2,3,3,3,4,5,5]
and I want to count the number of occurrences of each number, which I'm trying to do like so
[1,1,2,3,3,3,4,5,5].reduce(Hash.new(0)) { |hash,number| hash[number] += 1 }
The problem is I get the following error when I try to run it
NoMethodError: undefined method `[]=' for 1:Fixnum
from (irb):6:in `block in irb_binding'
from (irb):6:in `each'
from (irb):6:in `reduce'
from (irb):6
Am I able to set the initial value like this, or am I getting this wrong?
You can use each_with_object for cleaner syntax
[1,1,2,3,3,3,4,5,5].each_with_object(Hash.new(0)) { |number, hash| hash[number] += 1 }
Note that order of arguments is reverse i.e. |number, hash|
You can use reduce, but if you want so, you have to return the hash again:
[1,1,2,3,3,3,4,5,5].reduce(Hash.new(0)) do |hash,number|
hash[number] += 1
hash
end
Without that the value of hash[number] would be returned. The value of a hash assignment is the value itself. The block builds on the value you have returned previously.
Which means, that after the first it would try something like this: 1[1] += 1, which of course does not work, because Fixnums do not implement the method []=.
I like to use reduce with hash.update. It returns the array and I do not need the ; hash part:
[1,1,2,3,3,3,4,5,5].reduce(Hash.new(0)) { |h, n| h.update(n => h[n].next) }
Your question has been answered, but here's another way to skin the cat:
[Edited to adopt #David's suggestion.]
a = [1,1,2,3,3,3,4,5,5]
Hash[a.group_by(&:to_i).map {|g| [g.first, g.last.size]}]
This also works:
a.group_by{|e| e}.inject({}) {|h, (k,v)| h[k] = v.size; h}
and can be improved by adopting #spickermann's use of update (or it's synonym, merge!), to get rid of that irritating ; h at the end:
a.group_by{|e| e}.inject({}) {|h, (k,v)| h.merge!(k => v.size)}
Formerly, I had:
Hash[*a.group_by(&:to_i).to_a.map {|g| [g.first, g.last.size]}.flatten]
I don't like to_i here. I wanted to use to_proc instead of ...group_by {|x| x|} (as used in one of the solutions above) and was looking for a method m that returns the receiver or its value. to_i was the best I could do (e.g., 2.to_i => 2), but only for Integers. Can anyone suggest a method that returns the receiver for a wide range of objects whose use with to_proc makes it's purpose obvious?
That's simple:
[1,1,2,3,3,3,4,5,5].group_by {|e| e}.collect {|k,v| [k,v.count]}
#=> [[1, 2], [2, 1], [3, 3], [4, 1], [5, 2]]
if result required in a Hash object
Hash[[1,1,2,3,3,3,4,5,5].group_by {|e| e}.collect {|k,v| [k,v.count]}]
#=> {1=>2, 2=>1, 3=>3, 4=>1, 5=>2}

ruby: how to convert hash into array

I have a hash that contains numbers as such:
{0=>0.07394653730860076, 1=>0.0739598476853163, 2=>0.07398647083461522}
it needs to be converted into an array like:
[[0, 0.07394653730860076], [1, 0.0739598476853163], [2, 0.07398647083461522]]
i tried my hash.values which gets me:
[0.07398921877505593, 0.07400253683443543, 0.07402917535044515]
I have tried multiple ways but i just started learning ruby.
try this:
{0=>0.07394653730860076, 1=>0.0739598476853163, 2=>0.07398647083461522}.to_a
#=> [[0, 0.07394653730860076], [1, 0.0739598476853163], [2, 0.07398647083461522]]
Definitely use the Hash#to_a method, which will produce exactly what you are looking for.
{0=>0.07394653730860076, 1=>0.0739598476853163, 2=>0.07398647083461522}.to_a
=> [[0, 0.07394653730860076], [1, 0.0739598476853163], [2, 0.07398647083461522]]
Hash#values will give you only the values of each element in the hash, while Hash#keys will give you just the keys. Fortunately, the default behavior of to_a is what you are looking for.

Put the index first with each_with_index

I am using Array() with each_with_index to output a array with index but I want it to output
[[0,obj1],[1,obj2]....]
whereas each_with_index makes it output
[[obj1,0],[obj2,1]....]
Is there anyway this can be fixed easilly?
As been asked to show code.
Array(test.each_with_index)
Try adding .map { |x| x.reverse } after the each_with_index.
Use this:
[obj1, obj2, ..., objN].map.with_index{|a,i| [i,a] }
#=> [[0, obj1], [1, obj2], ..., [N-1, objN]]

How do I get the unique elements from an array of hashes in Ruby?

I have an array of hashes, and I want the unique values out of it. Calling Array.uniq doesn't give me what I expect.
a = [{:a => 1},{:a => 2}, {:a => 1}]
a.uniq # => [{:a => 1}, {:a => 2}, {:a => 1}]
Where I expected:
[{:a => 1}, {:a => 2}]
In searching around on the net, I didn't come up with a solution that I was happy with. Folks recommended redefining Hash.eql? and Hash.hash, since that is what Array.uniq is querying.
Edit:
Where I ran into this in the real world, the hashes were slightly more complex. They were the result of parsed JSON that had multiple fields, some of which the values were hashes as well. I had an array of those results that I wanted to filter out the unique values.
I don't like the redefine Hash.eql? and Hash.hash solution, because I would either have to redefine Hash globally, or redefine it for each entry in my array. Changing the definition of Hash for each entry would be cumbersome, especially since there may be nested hashes inside of each entry.
Changing Hash globally has some potential, especially if it were done temporarily. I'd want to build another class or helper function that wrapped saving off the old definitions, and restoring them, but I think this adds more complexity than is really needed.
Using inject seems like a good alternative to redefining Hash.
I can get what I want by calling inject
a = [{:a => 1},{:a => 2}, {:a => 1}]
a.inject([]) { |result,h| result << h unless result.include?(h); result }
This will return:
[{:a=>1}, {:a=>2}]
Ruby 1.8.7+ will return just what you have expected:
[{:a=>1}, {:a=>2}, {:a=>1}].uniq
#=> [{:a=>1}, {:a=>2}]
I've had a similar situation, but hashes had keys. I used sorting method.
What I mean:
you have an array:
[{:x=>1},{:x=>2},{:x=>3},{:x=>2},{:x=>1}]
you sort it (#sort_by {|t| t[:x]}) and get this:
[{:x=>1}, {:x=>1}, {:x=>2}, {:x=>2}, {:x=>3}]
now a bit modified version of answer by Aaaron Hinni:
your_array.inject([]) do |result,item|
result << item if !result.last||result.last[:x]!=item[:x]
result
end
I've also tried:
test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]}
but it's very slow. here is my benchmark:
test=[]
1000.times {test<<{:x=>rand}}
Benchmark.bmbm do |bm|
bm.report("sorting: ") do
test.sort_by {|t| t[:x]}.inject([]) {|r,h| r<<h if !r.last||r.last[:x]!=h[:x]; r}
end
bm.report("inject: ") {test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]} }
end
results:
Rehearsal ---------------------------------------------
sorting: 0.010000 0.000000 0.010000 ( 0.005633)
inject: 0.470000 0.140000 0.610000 ( 0.621973)
------------------------------------ total: 0.620000sec
user system total real
sorting: 0.010000 0.000000 0.010000 ( 0.003839)
inject: 0.480000 0.130000 0.610000 ( 0.612438)
Assuming your hashes are always single key-value pairs, this will work:
a.map {|h| h.to_a[0]}.uniq.map {|k,v| {k => v}}
Hash.to_a creates an array of key-value arrays, so the first map gets you:
[[:a, 1], [:a, 2], [:a, 1]]
uniq on Arrays does what you want, giving you:
[[:a, 1], [:a, 2]]
and then the second map puts them back together as hashes again.
You can use (tested in ruby 1.9.3),
[{a: 1},{a: 2},{a:1}].uniq => [{a:1},{a: 2}]
[{a: 1,b: 2},{a: 2, b: 2},{a: 1, b: 3}].uniq_by {|v| v[:a]} => [{a: 1,b: 2},{a: 2, b: 2}]
The answer you give is similar to the one discussed here. It overrides the hash and eql? methods on the hashes that are to appear in the array which then makes uniq behave correctly.
found on google
http://mikeburnscoder.wordpress.com/2008/01/18/uniquify-an-array-of-hashes-in-ruby/
The pipe method on arrays (available since 1.8.6) performs set union (returning an array), so the following is another possible way to get unique elements of any array a:
[] | a

Resources