Accessing elements of a ruby hash - ruby-on-rails

Ruby 2.15
I defined the following hash:
test = Hash.new
test["foo"] = {
'id' => 5,
'lobbyist_id' => 19,
'organization_id' => 8
}
If I do
test.each do |t|
print t["id"]
end
I get:
TypeError: no implicit conversion of String into Integer
from (irb):1571:in `[]'
How do I access the elements, using an each loop?
Answer:
test.each do |t|
t.each do |t1|
puts t1["id"]
end
end

With a Hash, iteration is made through key first, then value. So have your block use what you need.
test.each do |key|
puts key
end
test.each do |key, value|
puts key
puts value
end
There are also
test.each_key do |key|
puts key
end
test.each_value do |value|
puts value
end
Sidenote: id is inside test["foo"], so maybe you'd need 2 loops
To get id from your hash directly:
test["foo"]["id"]
test["foo"].each {|k, v| puts "#{k}: #{v}" }

In your example we assume you've previously done:
test = Hash.new
In your example variable test is a hash and foo is a key who's value contains a hash of key values. If you want to target those, you'll need to loop over them
test['foo'].each do |k,v|
puts "my key is #{k}"
puts "it's value is {v}
end
If you want to do both at the same time:
test.each do |k,v|
puts "base hash key #{k}"
puts "base hash value #{v}"
v.each do |kk,vv|
puts "key #{kk}"
puts "value #{vv}"
end
end

Related

How can I create an Active Admin table in a custom page and insert JSON data?

I'm trying to build a table of JSON data in a custom page in Active Admin. The JSON response is fairly deeply nested so I'm using a lot of loops. Here's as far as I've been able to get/some of what I've tried:
panel "Boxes" do
boxes.each do |box| #isolate each hash item in the array
# table_for box.values do
box.each do |key, value|
if value.is_a?(Hash) #if value of a hash is a hash
columns do
value.each do |k, v| #iterate through the hash
text_node "#{k}: #{v}"
end
end
elsif value.is_a?(Array) #there's only one value that's an array & the key is "Products"
columns do
value.each do |products_array|
columns do
products_array.each do |k, v|
if v.is_a?(Hash)
v.each do |kk, vv|
if vv.is_a?(Hash)
vv.each do |kkk, vvv|
text_node "#{kkk}: #{vvv}, "
end
else
text_node "#{kk}: #{vv}, "
end
end
else
text_node "#{k}: #{v}, "
end
end
end
end
end
else
# key.each do
# column key
# end
end
end
# end
end
I'm looking for general guidelines as to how to make a table in a custom Active Admin page as well as how to access/display deeply nested array/hash attributes. Thanks!
Try a recursive helper to convert the JSON to the string you want:
def nested_hash_to_s(h)
h.map { |k, v| v.is_a?(Hash) nested_hash_to_s(v) : "#{k}: #{v}" }.join(", ")
end
then your column simplifies to:
columns do
text_node nested_hash_to_s(products_array)
end
There might be other issues, but that's a start.

Iterate through array of column names to pass to uniq.pluck()?

I have multiple columns I need to pull unique values from and compile an array of each unique value. Using uniq.pluck(:column_name) works, but how do I iterate over an array of column names?
react = []
fields = [reactivity_1, reactivity_2, reactivity_3, reactivity_4]
fields.each do |field|
puts "Parsing #{field}"
Raw.all.uniq.pluck(field).each do |r|
unless react.include? r
puts "Adding #{r} to Array."
react << r
else
puts "#{r} Exists."
end
end
end
Error:
NameError: undefined local variable or method `reactivity_1' for main:Object
You will need to make the column names strings or symbols, like this Ruby thinks it is a local varaible or method.
react = Set.new
fields = [:reactivity_1, :reactivity_2, :reactivity_3, :reactivity_4]
fields.each do |field|
puts "Parsing #{field}"
Raw.all.uniq.pluck(field).each do |r|
react << r
end
end
If you want to make sure that a collection does not contain duplicate, you can use a Set:
require "set"
set = Set.new
set << "foo"
set << "bar"
set << "bar"
puts set.size #> 2
I've rewritten your code sample to use Set.
Can you describe what you are trying to achieve? Perhaps there is an easier way to get the data out of the DB.

How do I iterate through a table and create a hash for each value?

I have two tables. One for accounts and another for keywords. I would like to iterate over all of the keywords and store each one in a hash--grouped by the account ID that added the keyword. The code that I have below doesn't add each keyword to the hash. For example, I have an account that has 2 keyword entries. My code skips the first entry and only adds the second entry to the hash.
#keyword_hash = {}
#account.each do |key, value|
#keywords.where(:profile_id => key).each do |keyword|
#keyword_hash[key] = keyword.entry
end
end
puts #keyword_hash
How can I modify the above code so that I add each keyword entry for a particular account to the hash?
I would like to be able to do #keyword_hash[6] and get keyword1, keyword2, keyword3, etc. for that account. Thanks!
Make an array [keyword1, keyword2, keyword3, etc.] and then add it to hash
**
#keyword_hash = {}
#account.each do |key, value|
arr = []
#keywords.where(:profile_id => key).each do |keyword|
arr << keyword.entry
end
#keyword_hash[key] = arr
end
puts #keyword_hash
**
Try this code
#keyword_hash = Hash.new { |h, k| h[k] = [] }
#account.each do |key, value|
#keywords.where(:profile_id => key).each do |keyword|
#keyword_hash[key] << keyword.entry
end
end
puts #keyword_hash
The mistake you are doing is that you are storing a single value against each key in your #keyword_hash hash. so when your code writes second value against account key, it replaces the previous value instead of adding second one.
Edit: Thank you #mudasobwa for correction regarding shared default value.
#keyword_hash = Hash.new { |hash, key| hash[key] = [] }
#keywords.group_by{ |k| k.profile_id }.each do |key,value|
#keyword_hash[key] = value.map(&:entry)
end
puts #keyword_hash
After doing some more research, I found the above solution. I used #jvillian's suggestion about group_by, and I found this article that showed me how to initialize the hash.

Adding assert function

I'm new to Ruby on Rails and going through Learn Ruby the Hard Way right now. In the lesson I am supposed to add the Assert feature to Dict.rb (shown below). Would anyone know how I can go about doing this? Every time I try I receive an error.
module Dict
#Creates a new function that makes a Dictionary. This is done through creating the
# aDict variable that has an array in which num_buckets array is placed inside.
# These buckets will be used to hold the contents of the Dict and later aDict.length
# is used to find out how many buckets there are.
def Dict.new(num_buckets=256)
# Initializes a Dict with the given number of buckets.
aDict = []
(0...num_buckets).each do |i|
aDict.push([])
end
return aDict
end
# Converts a string to a number using the bult-in Ruby 'hash' function
# Once I have a number for the key, I use the % operator and aDict.length to get a
# bucket where the remainder can go.
def Dict.hash_key(aDict, key)
# Given a key this will create a number and then convert it to an index for the
# aDict's buckets
return key.hash % aDict.length
end
# Uses hash_key to find a bucket that the key could be in. Using bucket_id I can get
# the bucket where the key could be. By using the modulus operator I know it will fit
# into the aDict array of 256.
def Dict.get_bucket(aDict, key)
# Given a key, find the bucket where it would go.
bucket_id = Dict.hash_key(aDict, key)
return aDict[bucket_id]
end
# Uses get_slot to get the (i, k, v) and returns the v (value) only.
def Dict.get_slot(aDict, key, default=nil)
# Returns the index, key and value of a slot found in a bucket.
bucket = Dict.get_bucket(aDict, key)
bucket.each_with_index do |kv, i|
k, v = kv
if key == k
return i, k, v
end
end
return -1, key, default
end
def Dict.get(aDict, key, default=nil)
# Gets the value in a bucket for the given key or the default.
i, k, v = Dict.get_slot(aDict, key, default=default)
return v
end
# Sets a key/value pair by getting the bucket and appending the new (key, value) to it.
# First you have to get the bucket, see if the key already exists, if it does then
# replace it, if it doesn't get replaced then append it.
def Dict.set(aDict, key, value)
# Sets the key to the value, replacing any existing value.
bucket = Dict.get_bucket(aDict, key)
i, k, v = Dict.get_slot(aDict, key)
if i >= 0
bucket[i] = [key, value]
else
bucket.push([key, value])
end
end
# Deletes a key by getting the bucket, searching for key in it and deleting it form the
# array.
def Dict.delete(aDict, key)
# Deletes the given key from the Dict.
bucket = Dict.get_bucket(aDict, key)
(0...bucket.length).each do |i|
k, v = bucket[i]
if key == k
bucket.delete_at(i)
break
end
end
end
# goes through each slot in each bucket and prints out what's in the Dict.
def Dict.list(aDict)
# Prints out what's in the Dict.
aDict.each do |bucket|
if bucket
bucket.each {|k, v| puts k, v}
end
end
end
end
I am using the following script to run methods from the module Dict.rb:
require './dict.rb'
# create a mapping of state to abbreviation
states = Dict.new()
Dict.set(states, 'Oregon', 'OR')
Dict.set(states, 'Florida', 'FL')
Dict.set(states, 'California', 'CA')
Dict.set(states, 'New York', 'NY')
Dict.set(states, 'Michigan', 'MI')
# create a basic set of states and some cities in them
cities = Dict.new()
Dict.set(cities, 'CA', 'San Francisco')
Dict.set(cities, 'MI', 'Detroit')
Dict.set(cities, 'FL', 'Jacksonville')
# add some more cities
Dict.set(cities, 'NY', 'New York')
Dict.set(cities, 'OR', 'Portland')
# puts out some cities
puts '-' * 10
puts "NY State has: #{Dict.get(cities, 'NY')}"
puts "OR State has: #{Dict.get(cities, 'OR')}"
# puts some states
puts '-' * 10
puts "Michigan's abbreviation is: #{Dict.get(states, 'Michigan')}"
puts "Florida's abbreviation is: #{Dict.get(states, 'Florida')}"
# do it by using the state then cities dict
puts '-' * 10
puts "Michigan has: #{Dict.get(cities, Dict.get(states, 'Michigan'))}"
puts "Florida has: #{Dict.get(cities, Dict.get(states, 'Florida'))}"
# puts every state abbreviation
puts '-' * 10
Dict.list(states)
# puts every city in state
puts '-' * 10
Dict.list(cities)
puts '-' * 10
# by default ruby says "nil" when something isn't in there
state = Dict.get(states, 'Texas')
if !state
puts "Sorry, no Texas."
end
# default values using ||= with the nil result
city = Dict.get(cities, 'TX', 'Does Not Exist')
puts "The city for the state 'TX' is: #{city}"
There is no "assert" method by default in Ruby. That's why you are getting an "undefined method" error. You either need to implement this method yourself or use a testing framework like Minitest or Rspec.
If you want to implement a simple assert method yourself it would be something like this:
def assert(first_item, second_item)
unless first_item == second_item
puts "[Error] #{first_item} does not equal #{second_item}"
end
end
And then use it like this:
assert(Dict.get(cities, 'NY'), 'New York')
The idea is that assertions help you make sure your code returns the expected output without having to check it manually.

Ruby on Rails: Iterate Through Nested Params

I want to iterate through submitted params. Params array could be up to three level. Currently I can iterate only first level like this.
params[:job].each do |v, k|
#do something with k value, only if it's string. If it's array then find its string value.
end
How do I iterate params when you don't know what you are expecting?
One easy way is to use is_a?
if v.is_a? Array
# do whatever
elsif v.is_a? String
# do whatever
else
# something else
end
Some kind of recursive solution might be best here, such as:
def handle_hash hash
hash.each do |k, v|
if v.is_a? Hash
handle_hash v
elsif v.is_a? String
# handle string
end
end
end
And then you can just call handle_hash params[:job]
def nested_params(nested_hash={})
nested_hash.each_pair do |k,v|
case v
when String, Fixnum then
dosomething(v)
when Hash then nested_params(v)
else raise ArgumentError, "Unhandled type #{v.class}"
end
end
end

Resources