Ruby, turn array of hashes into single hash - ruby

I have the following Array of Hashes:
a = [{:a => 1, :b => "x"}, {:a => 2, :b => "y"}]
I need to turn it into:
z={"x" => 1, "y" => 2}
or:
z={1 => "x", 2 => "y"}
Can I do this in a clean and functional way?

Something like this:
Hash[a.map(&:values)] # => {1=>"x", 2=>"y"}
if you want the other way:
Hash[a.map(&:values).map(&:reverse)] # => {"x"=>1, "y"=>2}
incorporating the suggestion from #squiguy:
Hash[a.map(&:values)].invert

Related

Combine keys and values in RUBY

Given a hash:
{:a => "123", :b => "345", :c => "678", :d => "910"}
Write a code that generates an array that combines the keys and values. So the resulting array should be:
["a123", "b345", "c678", "d910"]
I would do something like this:
{:a => "123", :b => "345", :c => "678", :d => "910"}.map { |k, v| "#{k}#{v}" }
#=> ["a123", "b345", "c678", "d910"]

||= with hash wrapped in a method

I put a hash in a method:
def example
#_hash ||= {:a => {}, :b => {}}
end
I call that hash from another method, and add key/value to the sub-hash like so:
example[:a][:c] = "test"
This will change the hash to:
{:a => {:c => "test"}, :b => {}}
I don't get why I can still update the :c hash by calling the method again like so:
example[:a][:c] = "test2" #=> {:a => {:c => "test2"}, :b => {}}
This is odd because the equals/or operator should not assign the values again if #_hash is not nil/false. So I assume we do example.[]= method to change the value of an individual hash key/value. If that's true, then why is it that, when I remove ||= from the hash method like so:
def example
#_hash = {:a => {}, :b => {}}
end
nothing is changed?
The reason for this is that, when you call the example method again, it correctly returns the following hash:
example #=> {:a => {:c => "test"}, :b => {}} # variable like syntax
example() #=> {:a => {:c => "test"}, :b => {}} # method like syntax
But, when you do this:
example[:a][:c] = "test2"
What you are really doing is:
hash = example() #=> {:a => {:c => "test"}, :b => {}}
hash[:a][:c] = "test2" #=> {:a => {:c => "test2"}, :b => {}}
And, therefore, the hash is updated.

How to sort lines of a hash in descending order according to one of the key-value pairs in Ruby?

I have the following hash:
lines[0] = {"a" => dog, "b" => 32, "c" =>555, "d" => 100}
lines[1] = {"a" => cat, "b" => 34, "c" =>554, "d" => 4542}
lines[2] = {"a" => bird, "b" => 31, "c" =>532435, "d" => 23}
I would like to sort the hash by "b" in descending order so that I end up with:
lines[0] = {"a" => cat, "b" => 34, "c" =>554, "d" => 4542}
lines[1] = {"a" => dog, "b" => 32, "c" =>555, "d" => 100}
lines[2] = {"a" => bird, "b" => 31, "c" =>532435, "d" => 23}
What would be the best way to achieve that? Is there a method in Ruby that does this for you?
You seem to have a peculiar case where lines is NOT an array but a Hash, so you want to reassign values for each key based on value's order, for that first create array out of Hash, sort that, and that create a Hash based on new index e.g.
lines = {}
lines[0] = {"a" => :dog, "b" => 32, "c" =>555, "d" => 100}
lines[1] = {"a" => :cat, "b" => 34, "c" =>554, "d" => 4542}
lines[2] = {"a" => :bird, "b" => 31, "c" =>532435, "d" => 23}
require 'pp'
pp Hash[lines.map.sort_by {|k,v| -v["b"]}.map.with_index {|v, index| [index, v[1]]}]
output is:
{0=>{"a"=>:cat, "b"=>34, "c"=>554, "d"=>4542},
1=>{"a"=>:dog, "b"=>32, "c"=>555, "d"=>100},
2=>{"a"=>:bird, "b"=>31, "c"=>532435, "d"=>23}}
That said, why lines is a Hash? it could better be represented by an Array
lines.sort_by! {|hash| -hash["b"]}
You can do
lines.sort! { |a, b| b["b"] <=> a["b"] }

Filter hash with JsonPath

I need to find a certain hash element where one of the keys is equal to a certain value. I've tried many ways and can't seem to figure it out with jsonpath gem.
Need to get tire tag where grip == 'bad'
require "jsonpath"
hash = {
:id => 1,
:cars => [
{:id => 1, :tire => {:grip => "good", :color => "black"}},
{:id => 2, :tire => {:grip => "bad", :color => "red"}},
{:id => 3, :tire => {:grip => "good", :color => "green"}}
]
}
puts JsonPath.on(hash, "$..tire[?(#['grip'] == 'bad')]").inspect
No results.
The [?()] filter only works for arrays (or at least for either arrays or hashes, not both at the same time). In order for it to work, I had to enclose the :tire hash in an array.
Original:
:tire => {:grip => "good", :color => "black"}
New:
:tire => [{:grip => "good", :color => "black"}]
That's a "fix" that works for me. It would be better if someone fixed the jsonpath gem to make it work for both arrays and hashes (of the same type and at the same time).
In JsonPath.on first argument must be json, not hash.
I can't do it over ruby, it is not fully correct solution. But may be this help you.
require 'jsonpath'
require 'json'
hash = {
:id => 1,
:cars => [
{:id => 1, :tire => {:grip => "good", :color => "black"}},
{:id => 2, :tire => {:grip => "bad", :color => "red"}},
{:id => 3, :tire => {:grip => "good", :color => "green"}}
]
}
json = hash.to_json
obj = JsonPath.new( "$..tire")[json]
result = obj.inject(Array.new){|res, x| res << x if x["grip"]=='bad'; res }
p result # [{"grip"=>"bad", "color"=>"red"}]

hash containing an array to array of hashes in ruby

I've read through quite a few of posts, but none seem to do just this, which is a bit tricky.
Say I have a hash that contains an array as one of its values.
hash = {
:a => 'one',
:arr => [
{:id => 'ten', :amount => 10, :b => 'two'},
{:id => 'twenty', :amount => 20, :b => 'two'},
{:id => 'apple', :amount => 7, :b => 'applesauce'}
],
:c => 3
}
I want to convert this to an array of hashes (which would be of the size of the contained array), as follows:
# => [
{:a => 'one', :id => 'ten', :amount => 10, :b => 'two', :c => 3},
{:a => 'one', :id => 'twenty', :amount => 20, :b => 'two', :c => 3},
{:a => 'one', :id => 'apple', :amount => 7, :b => 'applesauce', :c => 3}
]
The conversion should maintain whatever key/value pairs are inside and outside the array, and ideally I could pass in the key of the array to ask it perform the action:
flatten_hash_array(hash, :arr)
I realize that the Ruby flatten in the Array class is not what we need. Grasping for a verb! Any help would be appreciated.
This should do the job, barring validity checks.
def flatten_hash_array(hash, key)
hash[key].map {|entry| entry.merge(hash.reject {|k| k == key})}
end

Resources