Ruby, accessing a nested value in a hash - ruby-on-rails

I have the following hash. Using ruby, I want to get the value of "runs". I can't figure out how to do it. If I do my_hash['entries'], I can dig down that far. If I take that value and dig down lower, I get this error:
no implicit conversion of String into Integer:
{"id"=>2582, "entries"=>[{"id"=>"7", "runs"=>[{"id"=>2588, ...

Assuming that you want to lookup values by id, Array#detect comes to the rescue:
h = {"id"=>2582, "entries"=>[{"id"=>"7", "runs"=>[{"id"=>2588}]}]}
# ⇓⇓⇓⇓⇓⇓⇓ lookup element with id = 7
h['entries'].detect { |e| e['id'] == 7 }['runs']
.detect { |e| e['id'] == 2588 }
#⇒ { "id" => 2588 }

As you have an array inside the entries so you can access it using an index like this:
my_hash["entries"][0]["runs"]
You need to follow the same for accessing values inside the runs as it is also an array.
Hope this helps.

I'm not sure about your hash, as it's incomplete. So , guessing you have multiple run values like:
hash = {"id"=>2582, "entries"=>[{"id"=>"7", "runs"=>[{"id"=>2588}]},
{"id"=>"8", "runs"=>[{"id"=>2589}]},
{"id"=>"9", "runs"=>[{"id"=>2590}]}]}
Then, you can do
hash["entries"].map{|entry| entry["runs"]}
OUTPUT
[[{"id"=>2588}], [{"id"=>2589}], [{"id"=>2590}]]

Related

Refer to a hash where the key is an ip_address

I've got a model that I need to group by the :sending_ip, which is a "cidr" column in the database.
#count_hash = Webhook.group('sending_ip').count
Resulting in this hash:
{#<IPAddr: IPv4:213.32.165.239/255.255.255.255>=>127000, #<IPAddr: IPv4:153.92.251.118/255.255.255.255>=>228000}
I cannot figure out how to reference this type of key. Below are some examples of the ways that I've tried to call these keys. All of them return nil or error.
#count_hash[#<IPAddr: IPv4:213.32.165.239/255.255.255.255>]
#count_hash["#<IPAddr: IPv4:213.32.165.239/255.255.255.255>"]
#count_hash[<IPAddr: IPv4:213.32.165.239/255.255.255.255>]
#count_hash["#<IPAddr: IPv4:213.32.165.239/255.255.255.255>"]
Elsewhere in my app, I've got a simpler example that works great. The other example groups by esp, which results in this hash:
{"hotmail"=>1000, "gmail"=>354000}
The second hash, I can refer to easily
#count_hash["gmail"]
To obtain the expected result of 354000
How can I achieve this same functionality with the previous hash that was grouped by sending_ip? Thank you in advance for your insight.
This:
#<IPAddr: IPv4:213.32.165.239/255.255.255.255>
is the result of calling #inspect on an instance of IPAddr. So the keys are IPAddr instances and you can say:
ip = IPAddr.new('213.32.165.239')
#count_hash[ip]
# 127000
Or you could iterate over the hash:
#count_hash.each { |ip, n| ... }
or over its keys:
#count_hash.keys.each { |ip| ... }
depending on what you need to do. You could even convert the keys to strings if that's more convenient:
#count_hash = #count_hash.transform_keys(&:to_s)
# or
#count_hash.transform_keys!(&:to_s)

Generation of table and accessing elements of a Array of Hashes

I have the following Array of hashes in a rails application:
a = ["{\"ROW1\"=>{\"correct\"=>{\"h\"=>\"10\", \"m\"=>\"11\", \"l\"=>
\"12\"}, \"wrong\"=>{\"h\"=>\"2\", \"m\"=>\"2\", \"l\"=>\"4\"}, \"blank
\"=>{\"h\"=>\"2\", \"m\"=>\"4\", \"l\"=>\"3\"}}, \"ROW2\"=>{\"correct
\"=>{\"h\"=>\"2\", \"m\"=>\"4\", \"l\"=>\"4\"}, \"wrong\"=>{\"h
\"=>\"4\", \"m\"=>\"6\", \"l\"=>\"6\"}, \"blank\"=>{\"h\"=>\"7\",
\"m\"=>\"5\", \"l\"=>\"6\"}}, \"ROW3\"=>{\"correct\"=>{\"h\"=>\"4\",
\"m\"=>\"6\", \"l\"=>\"7\"}, \"wrong\"=>{\"h\"=>\"6\", \"m\"=>\"7\",
\"l\"=>\"5\"}, \"blank\"=>{\"h\"=>\"7\", \"m\"=>\"9\", \"l\"=>
\"3\"}}}"]
I want to access its elements and create a database table from it, in the following format
ROW1 correct h=10, m=11,l=12
wrong h=2, m=2,l=4
blank h=2, m=4,l=3
...and similar for ROW2 and ROW3.
How can I do that?
I tried to access a value using
a["ROW1"]["Correct"]["h"]
...but it returns a nil value.
How to access the values of this array of hashes?
you need to first convert the string to hash which can be done as follows:
require 'json'
a = ["{\"ROW1\"=>{\"correct\"=>{\"h\"=>\"10\", \"m\"=>\"11\", \"l\"=>
\"12\"}, \"wrong\"=>{\"h\"=>\"2\", \"m\"=>\"2\", \"l\"=>\"4\"}, \"blank
\"=>{\"h\"=>\"2\", \"m\"=>\"4\", \"l\"=>\"3\"}}, \"ROW2\"=>{\"correct
\"=>{\"h\"=>\"2\", \"m\"=>\"4\", \"l\"=>\"4\"}, \"wrong\"=>{\"h
\"=>\"4\", \"m\"=>\"6\", \"l\"=>\"6\"}, \"blank\"=>{\"h\"=>\"7\",
\"m\"=>\"5\", \"l\"=>\"6\"}}, \"ROW3\"=>{\"correct\"=>{\"h\"=>\"4\",
\"m\"=>\"6\", \"l\"=>\"7\"}, \"wrong\"=>{\"h\"=>\"6\", \"m\"=>\"7\",
\"l\"=>\"5\"}, \"blank\"=>{\"h\"=>\"7\", \"m\"=>\"9\", \"l\"=>
\"3\"}}}"
]
hash_string = a[0]
hash = JSON.parse hash_string.gsub("\n", '').gsub('=>', ':')
# you access the hash now:
hash["ROW1"]["correct"]["h"]
# => 10
Btw, please note that there is a typo. Instead of Correct, the key is correct with small c instead of capital C.
Hope it helps : )

Deleting a specific hash inside an array of hashes using a variable

I have an array of hashes like this:
"[{:id=>15, :name=>"Hockey", :num_of_boxes=>2, :total_price=>6.98}, {:id=>14, :name=>"Baseball", :num_of_boxes=>3, :total_price=>8.97}, {:id=>5, :name=>"Basketball", :num_of_boxes=>2, :total_price=>5.98}]"
In my controller I want to delete the hash where the ID is set as a variable elsewhere. How would I go about doing this?
This is what I was trying to get to work, and I think it's the right path? id would be set in the controller before this method runs:
new_array = eval(#garden.seed_cart).reject { |h| id.include? h['15'] }
Thanks for your help!
Try this:
arr.delete_if { |hash| id.include?(hash[:id]) }
Note (comparison to the #reject suggestion): #delete_if returns the unchanged array if there is nothing to delete. #reject returns nil. Also, #delete_if modifies the array in place, whereas #reject (without !) simply returns a changed version of the array but leaves the original unchanged.
edit
Note: the use of include? here assumes that there may be multiple integers that are being filtered out, i.e. if id (or ids) is actually an array. If you just need to filter one ID, you can use a straightforward comparison with hash[:id] == id inside the block. Thanks to smathy for pointing this out in the comments.
If you have this data:
data = [{:id=>15, :name=>"Hockey", :num_of_boxes=>2, :total_price=>6.98}, {:id=>14, :name=>"Baseball", :num_of_boxes=>3, :total_price=>8.97}, {:id=>5, :name=>"Basketball", :num_of_boxes=>2, :total_price=>5.98}]
And you want to remove this ids from data:
ids = [14,15]
You can do
new_array = data.reject { |h| ids.include? h[:id] }
And you get
new_array = [{:id=>5, :name=>"Basketball", :num_of_boxes=>2, :total_price=>5.98}]
Note: data in an array (not a string) and ids is an array too.

Iterate through dynamic JSON keys

Using:
JSON.parse(response.read_body)
Returned this hash:
{
"stats": {
"2012-10-31": 2637,
"2012-10-30": 2873,
"2012-12-14": 2894,
"2012-12-08": 2906,
"2012-12-09": 3357,
"2012-11-30": 4959
}
}
The problem is that the Date keys can be any date value.
I want the sum of all values.
If you're in Rails, you can just use Enumerable#sum:
JSON.parse(response.read_body)["stats"].values.sum
You can use Ruby Enumerable's reduce method. I haven't done any error handling in my example, but you might need to deal with bad or missing data if this is coming from an API.
JSON.parse(response.read_body)["stats"].reduce 0 do |memo, kvp|
memo + kvp.last
end
Edit:
Option 2 is more ninja:
JSON.parse(response.read_body)["stats"].values.reduce(&:+)

Why am I getting this TypeError - "can't convert Symbol into Integer"?

I have an array of hashes. Each entry looks like this:
- !map:Hashie::Mash
name: Connor H Peters
id: "506253404"
I'm trying to create a second array, which contains just the id values.
["506253404"]
This is how I'm doing it
second_array = first_array.map { |hash| hash[:id] }
But I'm getting this error
TypeError in PagesController#home
can't convert Symbol into Integer
If I try
second_array = first_array.map { |hash| hash["id"] }
I get
TypeError in PagesController#home
can't convert String into Integer
What am I doing wrong? Thanks for reading.
You're using Hashie, which isn't the same as Hash from ruby core. Looking at the Hashie github repo, it seems that you can access hash keys as methods:
first_array.map { |hash| hash.id }
Try this out and see if that works--make sure that it doesn't return the object_id. As such, you may want to double-check by doing first_array.map { |hash| hash.name } to see if you're really accessing the right data.
Then, provided it's correct, you can use a proc to get the id (but with a bit more brevity):
first_array.map(&:id)
This sounds like inside the map block that hash is not actually a hashie - it's an array for some reason.
The result is that the [] method is actually an array accessor method and requires an integer. Eg. hash[0] would be valid, but not hash["id"].
You could try:
first_array.flatten.map{|hash| hash.id}
which would ensure that if you do have any nested arrays that nesting is removed.
Or perhaps
first_array.map{|hash| hash.id if hash.respond_to?(:id)}
But either way you may end up with unexpected behaviour.

Resources