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
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.
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.
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.
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.
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