Accessing a hash with symbol or integer - ruby

I have this hash:
#current_user.birthday = { "birthday" => "12/01/1978", "id" => "524626626" }
I would like to grab the date "12/01/1978".
I tried #current_user.birthday[:birthday], only to get no implicit conversion of Symbol into Integer
What's the proper way?
EDIT
I think the output is a string instead
puts #current_user.birthday gives me {"birthday"=>"09/21/1985", "id"=>"425495284312580"}
puts #current_user.birthday[0..3] gives me {"bi
I can possibly get it by #current_user.birthday[14..23] which puts out 12/01/1978. But is there a better way, in the event there are multiple birthdays

It should be
#current_user.birthday["birthday"]
Your birthday isn't a symbol but string.

So the problem is with the Hash printed as a String. I used eval to convert it into a Hash
#current_user.birthday = {"birthday"=>"09/21/1985", "id"=>"425495284312580"}
birthday = #current_user.birthday
hash = eval(birthday)
puts hash["birthday"] => 12/01/1978

Related

Accidental Type conversion in Ruby with strings

I'm trying to solve a challenge where you take in a string of words but return the longest word in the string. My strategy is to break the string into an array of individual words and search the array. However, I'm getting a type conversion error. What is causing the type conversion error? This is particularly strange to me because I don't actually see any type conversion happening here.
def LongestWord(sen)
sen1 = sen.split("/\W+/")
grt = 0
sen1.each do |i|
if sen1[i].length > sen1[grt].length # Type conversion error
grt = i
end
end
sen1[grt]
end
# keep this function call here
puts LongestWord(STDIN.gets)
The type conversion is caused by the array entry i being converted (probably unsuccessfully) into an integer (though I suppose it could be ruby trying to convert the array into a hash, and use i as a key to the hash).
Your misunderstanding is that you think you're getting the array's indices passed into the block for each. What is passed in to that block is each individual value in the array. I.e., if your string sen is 'this is a silly string', then the values passed are 'this', 'is', 'a', 'silly', and 'string'.
You get the error because, when the code is running, i is the first value of sen1, which results in sen1['some string'] being evaluated.
An array can't have a string index, only a hash can, resulting in the Type error.
Meditate on this:
def longest_word(sen)
sen1 = sen.split # => ["foo", "barbaz"]
grt = 0
sen1.each do |i|
i # => "foo"
sen1 # => ["foo", "barbaz"]
sen1[i] # =>
sen1[grt] # =>
sen1[i].length # =>
sen1[grt].length # =>
if sen1[i].length > sen1[grt].length #Type conversion error
grt = i # =>
end
end
sen1[grt]
end
# keep this function call here
longest_word('foo barbaz')
Breaking it down further, here's the offending problem:
sen1 = 'foo barbaz'.split
sen1['foo'] # =>
# ~> TypeError
# ~> no implicit conversion of String into Integer
You don't see the type conversion, but it is there. In more than one place.
As Derrell Durrett pointed out in his answer, your are assuming (wrongly) that the index of the array is passed to the block, not its elements.
Then you write if sen1[i].length > sen1[grt].length. Let's consider the string is 'this is a silly string'. The first element is 'this' and what you are trying to do is if sen1['this'].length > sen1[0].length. As Ruby arrays always have integer indexes, Ruby tries to convert 'this' to an integer in order to find the element at the specified position. Of course this fails!
But your code is not that far from being right. A few small changes and it will run perfectly well:
def longest_word(sen)
sen1 = sen.split(" ")
grt = 0
sen1.each_index do |i|
if sen1[i].length > sen1[grt].length
grt = i
end
end
sen1[grt]
end
puts LongestWord(STDIN.gets)
Now you'd be passing the indexes with sen1.each_index and it'd be working fine.
Notice that I changed the name of your method to longest_word. This is much better, in fact, because this first capital letter is reserved to constants and class names.
I also would like to point that you are not using a good Ruby style. This could be written like this:
def longest_word(str)
str.split(" ").max_by{ |s| s.length }
end
and the result would be the same.

Hash key access via symbol not string [duplicate]

This question already has answers here:
Best way to convert strings to symbols in hash
(31 answers)
Closed 7 years ago.
I have the following code. I don't understand why table['something'] is different from table[:something].
require 'json'
data = '[{"type":"local","db":"all","user":"postgres","addr":null,"method":"ident"},{"type":"local","db":"all","user":"all","addr":null,"method":"ident"},{"type":"host","db":"all","user":"all","addr":"0.0.0.0/0","method":"md5"},{"type":"host","db":"all","user":"all","addr":"::1/128","method":"md5"},{"type":"local","db":"communicator","user":"communicator","addr":" ","method":"trust"}]'
table = JSON.parse(data)
table.each do | auth |
if (auth['user'])
print( '\n\n\n' + auth[:user] )
end
end
And on line with print( '\n\n\n' + auth[:user] ), I'm receiving an error:
TypeError: no implicit conversion of nil into String
from (irb):88:in `+'
This means that accessing table via :key is different from 'key'.
Why? How do I convert table to make it work with :key instead of 'key'?
The problem is that {"user":"postgres"} in JSON means:
{"user" => "postgres"}
in Ruby whereas in Ruby it means:
{:user => "postgres"}
Since you have it as JSON, you need to access the value as:
auth["user"]
and not as:
auth[:user]
which is nil since the hash lacks the key :user.
Because 'key' is a String and :key is a Symbol - those are two different things in Ruby.
It can be somewhat confusing, because :'key', or 'key': will also be a Symbol To make it work, just access Hash fields with a Symbol, like:
if (auth[:user])
To convert String indexed Hash to Symbol indexed Hash, refer to this question:
Best way to convert strings to symbols in hash
A Symbol :key is something other than a String 'key', just like a number 3 is something other than a String '3'. To convert a String to a key you can use key.to_sym
About the differance between the two other SO questions have been asked.
What's the difference between a string and a symbol in Ruby?
See also this article
EDIT
A solution to change your keys to symbol if you have no control over the source
require 'json'
data = '[{"type":"local","db":"all","user":"postgres","addr":null,"method":"ident"},{"type":"local","db":"all","user":"all","addr":null,"method":"ident"},{"type":"host","db":"all","user":"all","addr":"0.0.0.0/0","method":"md5"},{"type":"host","db":"all","user":"all","addr":"::1/128","method":"md5"},{"type":"local","db":"communicator","user":"communicator","addr":" ","method":"trust"}]'
table = JSON.parse(data)
p table
# [{"type"=>"local", "db"=>"all", "user"=>"postgres", "addr"=>nil, "method"=>"ident"}, {"type"=>"local", "db"=>"all", "user"=>"all", "addr"=>nil, "method"=>"ident"}, {"type"=>"host", "db"=>"all", "user"=>"all", "addr"=>"0.0.0.0/0", "method"=>"md5"}, {"type"=>"host", "db"=>"all", "user"=>"all", "addr"=>"::1/128", "method"=>"md5"}, {"type"=>"local", "db"=>"communicator", "user"=>"communicator", "addr"=>" ", "method"=>"trust"}]
table.map!{|auth|
new_auth = {}
auth.each{|k, v| new_auth[k.to_sym] = v}
new_auth
}
p table
# [{:type=>"local", :db=>"all", :user=>"postgres", :addr=>nil, :method=>"ident"}, {:type=>"local", :db=>"all", :user=>"all", :addr=>nil, :method=>"ident"}, {:type=>"host", :db=>"all", :user=>"all", :addr=>"0.0.0.0/0", :method=>"md5"}, {:type=>"host", :db=>"all", :user=>"all", :addr=>"::1/128", :method=>"md5"}, {:type=>"local", :db=>"communicator", :user=>"communicator", :addr=>" ", :method=>"trust"}]

Passing original string PLUS matches when using gsub and block?

Rails fans are familiar with params[:terms] or a hash of 'things' passed to the controller collected form the url. E.g.:
params
=> {"term"=>"Warren Buffet",
"controller"=>"search",
"format"=>"json",
"action"=>"index"}
If I want to use "Warren Buffet", "Warren" and "Buffet" in the code below, does anyone know which method I should be using instead? gsub is close, but it takes each match and not the original string too. Unless I'm doing it wrong, which is totally possible:
#potential_investors = []
params[:term].gsub(/(\w{1,})/) do |term|
#potential_investors &lt&lt User.where(:investor => true)
.order('first_name ASC, last_name ASC')
.search_potential_investors(term)
end
Thoughts?
How about:
s = "Filthy Rich"
s.split(" ").push(s)
>> ["Filthy", "Rich", "Filthy Rich"]
Or with scan if you prefer to use the regexp instead:
s.scan(/\w+/).push(s)
>> ["Filthy", "Rich", "Filthy Rich"]
params["term"].gsub(/(\w{1,})/)
returns an enumerator. You could convert it to an array and append the original term to it:
ary = params["term"].gsub(/(\w{1,})/).to_a + [params["term"]]
then process it:
ary.each do |term|
...

How to convert a ruby integer into a symbol

I have a Ruby array like this
q_id = [1,2,3,4,5,...,100]
I want to iterate through the array and convert into a hash like this
{
:1 => { #some hash} ,
:2 => { #another hash},
...
:100 => {#yet another hash}
}
What is the shortest and most elegant way to accomplish this?
[EDIT : the to_s.to_sym while being handy is not how I want it. Apologies for not mentioning it earlier.]
For creating a symbol, either of these work:
42.to_s.to_sym
:"#{42}"
The #inspect representation of these shows :"42" only because :42 is not a valid Symbol literal. Rest assured that the double-quotes are not part of the symbol itself.
To create a hash, there is no reason to convert the keys to symbols, however. You should simply do this:
q_id = (1..100).to_a
my_hash_indexed_by_value = {}
q_id.each{ |val| my_hash_indexed_by_value[val] = {} }
Or this:
my_hash = Hash[ *q_id.map{ |v| [v,{}] }.flatten ]
Or this:
# Every time a previously-absent key is indexed, assign and return a new hash
my_hash = Hash.new{ |h,val| h[val] = {} }
With all of these you can then index your hash directly with an integer and get a unique hash back, e.g.
my_hash[42][:foo] = "bar"
Unlike JavaScript, where every key to an object must be a string, Hashes in Ruby accept any object as the key.
To translate an integer into a symbol, use to_s.to_sym .. e.g.,:
1.to_s.to_sym
Note that a symbol is more related to a string than an integer. It may not be as useful for things like sorting anymore.
Actually "symbol numbers" aren't a thing in Ruby (try to call the to_sym method on a number). The benefit of using symbols in a hash is about performance, since they always have the same object_id (try to call object_id on strings, booleans, numbers, and symbols).
Numbers are immediate value and, like Symbol objects, they always have the same object_id.
Anyway, using the new hash syntax implies using symbols as keys, but you can always use the old good "hash rocket" syntax
awesome_hash = { 1 => "hello", 2 => "my friend" }
Read about immediate values here:
https://books.google.de/books?id=jcUbTcr5XWwC&pg=PA73&lpg=PA73&dq=immediate+values+singleton+method&source=bl&ots=fIFlAe8xjy&sig=j7WgTA1Cft0WrHwq40YdTA50wk0&hl=en&sa=X&ei=0kHSUKCVB-bW0gHRxoHQAg&redir_esc=y#v=onepage&q&f=false
If you are creating a hard-coded constant numeric symbol, there's a simpler way:
:'99'
This produces the same results as the more complex methods in other answers:
irb(main):001:0> :'99'
=> :"99"
irb(main):002:0> :"#{99}"
=> :"99"
irb(main):003:0> 99.to_s.to_sym
=> :"99"
Of course, this will not work if you're dynamically creating a symbol from a variable, in which case one of the other two approaches is required.
As already stated, :1 is not a valid symbol. Here's one way to do what you're wanting, but with the keys as strings:
Hash[a.collect{|n| [n.to_s, {}] }]
An array of the objects you want in your hash would be so much easier to use, wouldn't it? Even a hash of integers would work pretty well, wouldn't it?
u can use
1.to_s.to_sym
but this will make symbols like :"1"
You can make symbolic keys with Hash[]:
a = Hash[(1..100).map{ |x| ["#{x}".to_sym, {}] }]
Check type of hash keys:
puts a.keys.map(&:class)
=>
Symbol
...
Symbol
Symbol

How do I convert a String object into a Hash object?

I have a string which looks like a hash:
"{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }"
How do I get a Hash out of it? like:
{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }
The string can have any depth of nesting. It has all the properties how a valid Hash is typed in Ruby.
For different string, you can do it without using dangerous eval method:
hash_as_string = "{\"0\"=>{\"answer\"=>\"1\", \"value\"=>\"No\"}, \"1\"=>{\"answer\"=>\"2\", \"value\"=>\"Yes\"}, \"2\"=>{\"answer\"=>\"3\", \"value\"=>\"No\"}, \"3\"=>{\"answer\"=>\"4\", \"value\"=>\"1\"}, \"4\"=>{\"value\"=>\"2\"}, \"5\"=>{\"value\"=>\"3\"}, \"6\"=>{\"value\"=>\"4\"}}"
JSON.parse hash_as_string.gsub('=>', ':')
Quick and dirty method would be
eval("{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }")
But it has severe security implications.
It executes whatever it is passed, you must be 110% sure (as in, at least no user input anywhere along the way) it would contain only properly formed hashes or unexpected bugs/horrible creatures from outer space might start popping up.
The string created by calling Hash#inspect can be turned back into a hash by calling eval on it. However, this requires the same to be true of all of the objects in the hash.
If I start with the hash {:a => Object.new}, then its string representation is "{:a=>#<Object:0x7f66b65cf4d0>}", and I can't use eval to turn it back into a hash because #<Object:0x7f66b65cf4d0> isn't valid Ruby syntax.
However, if all that's in the hash is strings, symbols, numbers, and arrays, it should work, because those have string representations that are valid Ruby syntax.
I had the same problem. I was storing a hash in Redis. When retrieving that hash, it was a string. I didn't want to call eval(str) because of security concerns. My solution was to save the hash as a json string instead of a ruby hash string. If you have the option, using json is easier.
redis.set(key, ruby_hash.to_json)
JSON.parse(redis.get(key))
TL;DR: use to_json and JSON.parse
Maybe YAML.load ?
The solutions so far cover some cases but miss some (see below). Here's my attempt at a more thorough (safe) conversion. I know of one corner case which this solution doesn't handle which is single character symbols made up of odd, but allowed characters. For example {:> => :<} is a valid ruby hash.
I put this code up on github as well. This code starts with a test string to exercise all the conversions
require 'json'
# Example ruby hash string which exercises all of the permutations of position and type
# See http://json.org/
ruby_hash_text='{"alpha"=>{"first second > third"=>"first second > third", "after comma > foo"=>:symbolvalue, "another after comma > foo"=>10}, "bravo"=>{:symbol=>:symbolvalue, :aftercomma=>10, :anotheraftercomma=>"first second > third"}, "charlie"=>{1=>10, 2=>"first second > third", 3=>:symbolvalue}, "delta"=>["first second > third", "after comma > foo"], "echo"=>[:symbol, :aftercomma], "foxtrot"=>[1, 2]}'
puts ruby_hash_text
# Transform object string symbols to quoted strings
ruby_hash_text.gsub!(/([{,]\s*):([^>\s]+)\s*=>/, '\1"\2"=>')
# Transform object string numbers to quoted strings
ruby_hash_text.gsub!(/([{,]\s*)([0-9]+\.?[0-9]*)\s*=>/, '\1"\2"=>')
# Transform object value symbols to quotes strings
ruby_hash_text.gsub!(/([{,]\s*)(".+?"|[0-9]+\.?[0-9]*)\s*=>\s*:([^,}\s]+\s*)/, '\1\2=>"\3"')
# Transform array value symbols to quotes strings
ruby_hash_text.gsub!(/([\[,]\s*):([^,\]\s]+)/, '\1"\2"')
# Transform object string object value delimiter to colon delimiter
ruby_hash_text.gsub!(/([{,]\s*)(".+?"|[0-9]+\.?[0-9]*)\s*=>/, '\1\2:')
puts ruby_hash_text
puts JSON.parse(ruby_hash_text)
Here are some notes on the other solutions here
#Ken Bloom and #Toms Mikoss's solutions use eval which is too scary for me (as Toms rightly points out).
#zolter's solution works if your hash has no symbols or numeric keys.
#jackquack's solution works if there are no quoted strings mixed in with the symbols.
#Eugene's solution works if your symbols don't use all the allowed characters (symbol literals have a broader set of allowed characters).
#Pablo's solution works as long as you don't have a mix of symbols and quoted strings.
This short little snippet will do it, but I can't see it working with a nested hash. I think it's pretty cute though
STRING.gsub(/[{}:]/,'').split(', ').map{|h| h1,h2 = h.split('=>'); {h1 => h2}}.reduce(:merge)
Steps
1. I eliminate the '{','}' and the ':'
2. I split upon the string wherever it finds a ','
3. I split each of the substrings that were created with the split, whenever it finds a '=>'. Then, I create a hash with the two sides of the hash I just split apart.
4. I am left with an array of hashes which I then merge together.
EXAMPLE INPUT: "{:user_id=>11, :blog_id=>2, :comment_id=>1}"
RESULT OUTPUT: {"user_id"=>"11", "blog_id"=>"2", "comment_id"=>"1"}
I prefer to abuse ActiveSupport::JSON. Their approach is to convert the hash to yaml and then load it. Unfortunately the conversion to yaml isn't simple and you'd probably want to borrow it from AS if you don't have AS in your project already.
We also have to convert any symbols into regular string-keys as symbols aren't appropriate in JSON.
However, its unable to handle hashes that have a date string in them (our date strings end up not being surrounded by strings, which is where the big issue comes in):
string = '{'last_request_at' : 2011-12-28 23:00:00 UTC }'
ActiveSupport::JSON.decode(string.gsub(/:([a-zA-z])/,'\\1').gsub('=>', ' : '))
Would result in an invalid JSON string error when it tries to parse the date value.
Would love any suggestions on how to handle this case
works in rails 4.1 and support symbols without quotes {:a => 'b'}
just add this to initializers folder:
class String
def to_hash_object
JSON.parse(self.gsub(/:([a-zA-z]+)/,'"\\1"').gsub('=>', ': ')).symbolize_keys
end
end
Please consider this solution. Library+spec:
File: lib/ext/hash/from_string.rb:
require "json"
module Ext
module Hash
module ClassMethods
# Build a new object from string representation.
#
# from_string('{"name"=>"Joe"}')
#
# #param s [String]
# #return [Hash]
def from_string(s)
s.gsub!(/(?<!\\)"=>nil/, '":null')
s.gsub!(/(?<!\\)"=>/, '":')
JSON.parse(s)
end
end
end
end
class Hash #:nodoc:
extend Ext::Hash::ClassMethods
end
File: spec/lib/ext/hash/from_string_spec.rb:
require "ext/hash/from_string"
describe "Hash.from_string" do
it "generally works" do
[
# Basic cases.
['{"x"=>"y"}', {"x" => "y"}],
['{"is"=>true}', {"is" => true}],
['{"is"=>false}', {"is" => false}],
['{"is"=>nil}', {"is" => nil}],
['{"a"=>{"b"=>"c","ar":[1,2]}}', {"a" => {"b" => "c", "ar" => [1, 2]}}],
['{"id"=>34030, "users"=>[14105]}', {"id" => 34030, "users" => [14105]}],
# Tricky cases.
['{"data"=>"{\"x\"=>\"y\"}"}', {"data" => "{\"x\"=>\"y\"}"}], # Value is a `Hash#inspect` string which must be preserved.
].each do |input, expected|
output = Hash.from_string(input)
expect([input, output]).to eq [input, expected]
end
end # it
end
Here is a method using whitequark/parser which is safer than both gsub and eval methods.
It makes the following assumptions about the data:
Hash keys are assumed to be a string, symbol, or integer.
Hash values are assumed to be a string, symbol, integer, boolean, nil, array, or a hash.
# frozen_string_literal: true
require 'parser/current'
class HashParser
# Type error is used to handle unexpected types when parsing stringified hashes.
class TypeError < ::StandardError
attr_reader :message, :type
def initialize(message, type)
#message = message
#type = type
end
end
def hash_from_s(str_hash)
ast = Parser::CurrentRuby.parse(str_hash)
unless ast.type == :hash
puts "expected data to be a hash but got #{ast.type}"
return
end
parse_hash(ast)
rescue Parser::SyntaxError => e
puts "error parsing hash: #{e.message}"
rescue TypeError => e
puts "unexpected type (#{e.type}) encountered while parsing: #{e.message}"
end
private
def parse_hash(hash)
out = {}
hash.children.each do |node|
unless node.type == :pair
raise TypeError.new("expected child of hash to be a `pair`", node.type)
end
key, value = node.children
key = parse_key(key)
value = parse_value(value)
out[key] = value
end
out
end
def parse_key(key)
case key.type
when :sym, :str, :int
key.children.first
else
raise TypeError.new("expected key to be either symbol, string, or integer", key.type)
end
end
def parse_value(value)
case value.type
when :sym, :str, :int
value.children.first
when :true
true
when :false
false
when :nil
nil
when :array
value.children.map { |c| parse_value(c) }
when :hash
parse_hash(value)
else
raise TypeError.new("value of a pair was an unexpected type", value.type)
end
end
end
and here are some rspec tests verifying that it works as expected:
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe HashParser do
describe '#hash_from_s' do
subject { described_class.new.hash_from_s(input) }
context 'when input contains forbidden types' do
where(:input) do
[
'def foo; "bar"; end',
'`cat somefile`',
'exec("cat /etc/passwd")',
'{:key=>Env.fetch("SOME_VAR")}',
'{:key=>{:another_key=>Env.fetch("SOME_VAR")}}',
'{"key"=>"value: #{send}"}'
]
end
with_them do
it 'returns nil' do
expect(subject).to be_nil
end
end
end
context 'when input cannot be parsed' do
let(:input) { "{" }
it 'returns nil' do
expect(subject).to be_nil
end
end
context 'with valid input' do
using RSpec::Parameterized::TableSyntax
where(:input, :expected) do
'{}' | {}
'{"bool"=>true}' | { 'bool' => true }
'{"bool"=>false}' | { 'bool' => false }
'{"nil"=>nil}' | { 'nil' => nil }
'{"array"=>[1, "foo", nil]}' | { 'array' => [1, "foo", nil] }
'{foo: :bar}' | { foo: :bar }
'{foo: {bar: "bin"}}' | { foo: { bar: "bin" } }
end
with_them do
specify { expect(subject).to eq(expected) }
end
end
end
end
I built a gem hash_parser that first checks if a hash is safe or not using ruby_parser gem. Only then, it applies the eval.
You can use it as
require 'hash_parser'
# this executes successfully
a = "{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' },
:key_b => { :key_1b => 'value_1b' } }"
p HashParser.new.safe_load(a)
# this throws a HashParser::BadHash exception
a = "{ :key_a => system('ls') }"
p HashParser.new.safe_load(a)
The tests in https://github.com/bibstha/ruby_hash_parser/blob/master/test/test_hash_parser.rb give you more examples of the things I've tested to make sure eval is safe.
This method works for one level deep hash
def convert_to_hash(str)
return unless str.is_a?(String)
hash_arg = str.gsub(/[^'"\w\d]/, ' ').squish.split.map { |x| x.gsub(/['"]/, '') }
Hash[*hash_arg]
end
example
> convert_to_hash("{ :key_a => 'value_a', :key_b => 'value_b', :key_c => '' }")
=> {"key_a"=>"value_a", "key_b"=>"value_b", "key_c"=>""}
I came to this question after writing a one-liner for this purpose, so I share my code in case it helps somebody. Works for a string with only one level depth and possible empty values (but not nil), like:
"{ :key_a => 'value_a', :key_b => 'value_b', :key_c => '' }"
The code is:
the_string = '...'
the_hash = Hash.new
the_string[1..-2].split(/, /).each {|entry| entryMap=entry.split(/=>/); value_str = entryMap[1]; the_hash[entryMap[0].strip[1..-1].to_sym] = value_str.nil? ? "" : value_str.strip[1..-2]}
Ran across a similar issue that needed to use the eval().
My situation, I was pulling some data from an API and writing it to a file locally. Then being able to pull the data from the file and use the Hash.
I used IO.read() to read the contents of the file into a variable. In this case IO.read() creates it as a String.
Then used eval() to convert the string into a Hash.
read_handler = IO.read("Path/To/File.json")
puts read_handler.kind_of?(String) # Returns TRUE
a = eval(read_handler)
puts a.kind_of?(Hash) # Returns TRUE
puts a["Enter Hash Here"] # Returns Key => Values
puts a["Enter Hash Here"].length # Returns number of key value pairs
puts a["Enter Hash Here"]["Enter Key Here"] # Returns associated value
Also just to mention that IO is an ancestor of File. So you can also use File.read instead if you wanted.
I had a similar issue when trying to convert a string to a hash in Ruby.
The result from my computations was this:
{
"coord":{"lon":24.7535,"lat":59.437},
"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],
"base":"stations",
"main":{"temp":283.34,"feels_like":281.8,"temp_min":282.33,"temp_max":283.34,"pressure":1021,"humidity":53},
"visibility":10000,
"wind":{"speed":3.09,"deg":310},
"clouds":{"all":75},
"dt":1652808506,
"sys":{"type":1,"id":1330,"country":"EE","sunrise":1652751796,"sunset":1652813502},
"timezone":10800,"id":588409,"name":"Tallinn","cod":200
}
I checked the type value and confirmed that it was of the String type using the command below:
result =
{
"coord":{"lon":24.7535,"lat":59.437},
"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],
"base":"stations",
"main":{"temp":283.34,"feels_like":281.8,"temp_min":282.33,"temp_max":283.34,"pressure":1021,"humidity":53},
"visibility":10000,
"wind":{"speed":3.09,"deg":310},
"clouds":{"all":75},
"dt":1652808506,
"sys":{"type":1,"id":1330,"country":"EE","sunrise":1652751796,"sunset":1652813502},
"timezone":10800,"id":588409,"name":"Tallinn","cod":200
}
puts result.instance_of? String
puts result.instance_of? Hash
Here's how I solved it:
All I had to do was run the command below to convert it from a String to a Hash:
result_new = JSON.parse(result, symbolize_names: true)
And then checked the type value again using the commands below:
puts result_new.instance_of? String
puts result_new.instance_of? Hash
This time it returned true for the Hash

Resources