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
Related
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"]
I have two arrays of hashes.
burgers = [
{:id => 1, :name => "cheese burger"},
{:id => 2, :name => "royale"},
{:id => 3, :name => "big mac"},
{:id => 4, :name => "angus beef"}
]
eaten = [
{:burger_id => 1},
{:burger_id => 2}
]
I would like to return an array or uneaten burgers, where burgers[:id] does not equal eaten[:burger_id]. In burgers_not_eaten_method, I have the expected return value.
def burgers_not_eaten
#Not sure how to compare burger[:id] with eaten[:burger_id]
burgers.reject { |burger| burger[:id] == #eaten burger_id }
# Expected: [{:id => 3, :name => "big mac"},{:id => 4, :name => "angus beef"}]
end
You're close, to make it easy I'd snag all the "eaten" ids into an array, and check for inclusion in that array, like so:
BURGERS = [
{:id => 1, :name => "cheese burger"},
{:id => 2, :name => "royale"},
{:id => 3, :name => "big mac"},
{:id => 4, :name => "angus beef"}
]
EATEN = [
{:burger_id => 1},
{:burger_id => 2}
]
def burgers_not_eaten
eaten_ids = EATEN.map { |e| e[:burger_id] }
BURGERS.reject { |burger| eaten_ids.include?(burger[:id]) }
end
burgers_not_eaten
# => [{:id=>3, :name=>"big mac"}, {:id=>4, :name=>"angus beef"}]
I have an array of hashes where I need to find and store matches based on one matching value between the hashes.
a = [{:id => 1, :name => "Jim", :email => "jim#jim.jim"},
{:id => 2, :name => "Paul", :email => "paul#paul.paul"},
{:id => 3, :name => "Tom", :email => "tom#tom.tom"},
{:id => 1, :name => "Jim", :email => "jim#jim.jim"},
{:id => 5, :name => "Tom", :email => "tom#tom.tom"},
{:id => 6, :name => "Jim", :email => "jim#jim.jim"}]
So I would want to return
b = [{:id => 1, :name => "Jim", :email => "jim#jim.jim"},
{:id => 3, :name => "Tom", :email => "tom#tom.tom"},
{:id => 5, :name => "Tom", :email => "tom#tom.tom"},
{:id => 6, :name => "Jim", :email => "jim#jim.jim"}]
Notes: I can sort the data (csv) by :name after the fact so they don't have to be nicely grouped, just accurate. Also it's not necessary two of the same, it could be 3 or 10 or more.
Also, the data is about 22,000 rows.
I tested this and it will do exactly what you want:
b = a.group_by { |h| h[:name] }.values.select { |a| a.size > 1 }.flatten
However, you might want to look at some of the intermediate objects produced in that calculation and see if those are more useful to you.
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
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"}]