Fetch key values directly from a custom hash format - ruby

I have a hash as shown below:
hash_ex = {:"p, q," => "1, 2,"}
And hash_ex[:p] returns nil:
hash_ex[:p]
# => nil
Instead, it should be 1. How would I be able to get this key value?

There is no defined key :p in hash_ex. Therefore nil is expected result.
hash_ex = {:"p, q,"=>"1, 2,"}
hash_ex.keys
# => [:"p, q,"]
hash_ex[:"p, q,"]
# => "1, 2,"
The above example shows that you have declared "p, q," as a key. Also "p, q," here is being treated as a Symbol and not a String. Therefore hash_ex["p, q,"] will also return nil.

But hash_ex[:p] returns nil instead it should be 1
Ruby is not an AI, it can't figure out how to parse your "custom Hash format".
How I would be able to get this key value?
Well, you have to convert your format into a structure that Ruby understands:
key_string = "p, q,"
value_string = "1, 2,"
keys = key_string.split(/,\s*/).map(&:to_sym) #=> [:p, :q]
values = value_string.split(/,\s*/).map(&:to_i) #=> [1, 2]
hash_ex = keys.zip(values).to_h
hash_ex[:p] #=> 1

Related

How to produce a value on that hash corresponding to the key, or nil by default

I ran into a bit of an road block on a beginner exercise for hashes in Ruby. I have the following problem to solve:
Create a method call read_from_hash that takes in two parameters. The first parameter is a hash, the second is a key. Used together, they will either produce a value on that hash corresponding to the key, or nil by default. Use these two parameters in conjunction to do just that.
Here's my code:
def read_from_hash(hash, key)
hash = {key => "value"}
hash(key)
end
Here's the error:
Failure/Error: expect(read_from_hash({name: 'Steve'}, :name)).to eq('Steve')
ArgumentError:
wrong number of arguments (given 1, expected 0)
What you want is simply:
def read_from_hash(hash, key)
hash[key]
end
h = {a: 1, b: 2}
read_from_hash(h, :a)
#=> 1
read_from_hash(h, :c)
#=> nil
Or for your example:
read_from_hash({name: 'Steve'}, :name)
#=> 'Steve'
Your current code:
hash = {key => "value"}
creates a new hash variable, overwriting the one that's being passed in through the params, while here:
hash(key)
you're trying to access the value of the element with the key key using regular parentheses () instead of brackets []. Because of that, what is is actually happening is that you're calling a #hash method and passing it the key variable as a parameter.

How can either convert a string into a hash or add it into a hash

So in basic Ruby I am trying to figure out how to either convert a string into a hash or put a string into a hash. I want the pokemon item as the key and a integer for the value.
Something like this:
hash = {}
pokemon_list = "pikachu charizard jigglypuff bulbasaur"
def create_poke_list(string)
hash << string.split
end
create_poke_list
Expected output:
hash
#=> {"pikachu"=>0, "charizard"=>0, "jigglypuff"=>0, "bulbasaur"=>0}
pokemon_list.split.product([0]).to_h
#=> {"pikachu"=>0, "charizard"=>0, "jigglypuff"=>0, "bulbasaur"=>0}
The steps:
a = pokemon_list.split
#=> ["pikachu", "charizard", "jigglypuff", "bulbasaur"]
b = a.product([0])
#=> [["pikachu", 0], ["charizard", 0], ["jigglypuff", 0], ["bulbasaur", 0]]
b.to_h
#=> <hash shown above>
Alternatively,
Hash[pokemon_list.split.product([0])]
Here Array#product is just a short-hand form of pokeman_list.zip(a) where a is an array consisting of pokenman_list.size equal elements, here zero. See Enumerable#zip also.
Or use String#gsub!
This is another way that does not require the string to be converted to an array.
pokemon_list.gsub(/[[:alpha:]]+/).with_object({}) { |w,h| h[w] = 0 }
#=> {"pikachu"=>0, "charizard"=>0, "jigglypuff"=>0, "bulbasaur"=>0}
This works because gsub returns an enumerator when executed without a block. It's admittedly an unusual use of that method (since it does not replace in characters in the string), but one that I've found useful at times.
If you need a hash that is initialised with default value of 0, you could simply do
hash = Hash.new(0)
p hash["pikachu"]
#=> 0

Ruby how to return an element of a dictionary?

# dictionary = {"cat"=>"Sam"}
This a return a key
#dictionary.key(x)
This returns a value
#dictionary[x]
How do I return the entire element
"cat"=>"Sam"
#dictionary
should do the trick for you
whatever is the last evaluated expression in ruby is the return value of a method.
If you want to return the hash as a whole. the last line of the method should look like the line I have written above
Your example is a bit (?) misleading in a sense it only has one pair (while not necessarily), and you want to get one pair. What you call a "dictionary" is actually a hashmap (called a hash among Rubyists).
A hashrocket (=>) is a part of hash definition syntax. It can't be used outside it. That is, you can't get just one pair without constructing a new hash. So, a new such pair would look as: { key => value }.
So in order to do that, you'll need a key and a value in context of your code somewhere. And you've specified ways to get both if you have one. If you only have a value, then:
{ #dictionary.key(x) => x }
...and if just a key, then:
{ x => #dictionary[x] }
...but there is no practical need for this. If you want to process each pair in a hash, use an iterator to feed each pair into some code as an argument list:
#dictionary.each do |key, value|
# do stuff with key and value
end
This way a block of code will get each pair in a hash once.
If you want to get not a hash, but pairs of elements it's constructed of, you can convert your hash to an array:
#dictionary.to_a
# => [["cat", "Sam"]]
# Note the double braces! And see below.
# Let's say we have this:
#dictionary2 = { 1 => 2, 3 => 4}
#dictionary2[1]
# => 2
#dictionary2.to_a
# => [[1, 2], [3, 4]]
# Now double braces make sense, huh?
It returns an array of pairs (which are arrays as well) of all elements (keys and values) that your hashmap contains.
If you wish to return one element of a hash h, you will need to specify the key to identify the element. As the value for key k is h[k], the key-value pair, expressed as an array, is [k, h[k]]. If you wish to make that a hash with a single element, use Hash[[[k, h[k]]]].
For example, if
h = { "cat"=>"Sam", "dog"=>"Diva" }
and you only wanted to the element with key "cat", that would be
["cat", h["cat"]] #=> ["cat", "Sam"]
or
Hash[[["cat", h["cat"]]]] #=> {"cat"=>"Sam"}
With Ruby 2.1 you could alternatively get the hash like this:
[["cat", h["cat"]]].to_h #=> {"cat"=>"Sam"}
Let's look at a little more interesting case. Suppose you have an array arr containing some or all of the keys of a hash h. Then you can get all the key-value pairs for those keys by using the methods Enumerable#zip and Hash#values_at:
arr.zip(arr.values_at(*arr))
Suppose, for example,
h = { "cat"=>"Sam", "dog"=>"Diva", "pig"=>"Petunia", "owl"=>"Einstein" }
and
arr = ["dog", "owl"]
Then:
arr.zip(h.values_at(*arr))
#=> [["dog", "Diva"], ["owl", "Einstein"]]
In steps:
a = h.values_at(*arr)
#=> h.values_at(*["dog", "owl"])
#=> h.values_at("dog", "owl")
#=> ["Diva", "Einstein"]
arr.zip(a)
#=> [["dog", "Diva"], ["owl", "Einstein"]]
To instead express as a hash:
Hash[arr.zip(h.values_at(*arr))]
#=> {"dog"=>"Diva", "owl"=>"Einstein"}
You can get the key and value in one go - resulting in an array:
#h = {"cat"=>"Sam", "dog"=>"Phil"}
key, value = p h.assoc("cat") # => ["cat", "Sam"]
Use rassoc to search by value ( .rassoc("Sam") )

Ruby: Reading a json file get `[]': can't convert String into Integer (TypeError)

I'm working on a Ruby code that receive a json file (from Flurry API) and create another json friendly-format for "Status Board" app. When using this json fragment:
"country":[{"#country":"US","day":[{"#date":"2013-09-20","#value":"1"},
{"#date":"2013-09- 23","#value":"1"}]},
{"#country":"KR","day":{"#date":"2013-09-20","#value":"1"}}
Everything works fine, but when the code read "KR" and then "day" (note the lack of "[]") I receive the following error:
`[]': can't convert String into Integer (TypeError)
The code I use to read the original json file is:
countries = response.parsed_response["country"]
countries.each do |datapoint|
countryName = datapoint["#country"]
days = datapoint["day"]
data = []
days.each do |datapoint2|
value = datapoint2["#value"]
date = Date.parse(datapoint2["#date"])
dateString = date.strftime(dateFormat)
data << { :title => dateString, :value => value }
end
dataSequences << { :title => countryName, :color => metric[:color], :datapoints => data
}
end
I'm kind of noob in Ruby, so...It is possible to add brackets when reading a json file to avoid this error?
Ruby includes a few type coercion methods that you might find useful.
The first is Array(), which will convert its argument into an array, unless the argument is already one, it which case it simply returns the argument:
Array(1) #=> [1]
Array([1,2,3]) #=> [1,2,3]
Somewhat annoyingly, when fed a single hash, it converts the hash into an array of arrays (the key-value pairs in the hash):
Array({ a: 1, b: 2}) #=> [[:a, 1], [:b, 2]]
However, there is also a Hash[] method that will convert that result back into a hash, but leave existing hashes alone:
Hash[ [[:a, 1], [:b, 2]] ] #=> { a: 1, b: 2 }
Since your 'day' key is sometimes an array of hashes and sometimes a single hash, you can use these in your method to ensure your iterations and lookups work as expected:
Array(datapoint["day"]).each do |datapoint2|
dp2_hash = Hash[datapoint2]
# ... etc, substituting future mentions of datapoint2 with dp2_hash

How to find a hash key containing a matching value

Given I have the below clients hash, is there a quick ruby way (without having to write a multi-line script) to obtain the key given I want to match the client_id? E.g. How to get the key for client_id == "2180"?
clients = {
"yellow"=>{"client_id"=>"2178"},
"orange"=>{"client_id"=>"2180"},
"red"=>{"client_id"=>"2179"},
"blue"=>{"client_id"=>"2181"}
}
Ruby 1.9 and greater:
hash.key(value) => key
Ruby 1.8:
You could use hash.index
hsh.index(value) => key
Returns the key for a given value. If
not found, returns nil.
h = { "a" => 100, "b" => 200 }
h.index(200) #=> "b"
h.index(999) #=> nil
So to get "orange", you could just use:
clients.key({"client_id" => "2180"})
You could use Enumerable#select:
clients.select{|key, hash| hash["client_id"] == "2180" }
#=> [["orange", {"client_id"=>"2180"}]]
Note that the result will be an array of all the matching values, where each is an array of the key and value.
You can invert the hash. clients.invert["client_id"=>"2180"] returns "orange"
You could use hashname.key(valuename)
Or, an inversion may be in order. new_hash = hashname.invert will give you a new_hash that lets you do things more traditionally.
try this:
clients.find{|key,value| value["client_id"] == "2178"}.first
According to ruby doc http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-key key(value) is the method to find the key on the base of value.
ROLE = {"customer" => 1, "designer" => 2, "admin" => 100}
ROLE.key(2)
it will return the "designer".
From the docs:
(Object?) detect(ifnone = nil) {|obj| ... }
(Object?) find(ifnone = nil) {|obj| ... }
(Object) detect(ifnone = nil)
(Object) find(ifnone = nil)
Passes each entry in enum to block. Returns the first for which block is not false. If no object matches, calls ifnone and returns its result when it is specified, or returns nil otherwise.
If no block is given, an enumerator is returned instead.
(1..10).detect {|i| i % 5 == 0 and i % 7 == 0 } #=> nil
(1..100).detect {|i| i % 5 == 0 and i % 7 == 0 } #=> 35
This worked for me:
clients.detect{|client| client.last['client_id'] == '2180' } #=> ["orange", {"client_id"=>"2180"}]
clients.detect{|client| client.last['client_id'] == '999999' } #=> nil
See:
http://rubydoc.info/stdlib/core/1.9.2/Enumerable#find-instance_method
The best way to find the key for a particular value is to use key method that is available for a hash....
gender = {"MALE" => 1, "FEMALE" => 2}
gender.key(1) #=> MALE
I hope it solves your problem...
Another approach I would try is by using #map
clients.map{ |key, _| key if clients[key] == {"client_id"=>"2180"} }.compact
#=> ["orange"]
This will return all occurences of given value. The underscore means that we don't need key's value to be carried around so that way it's not being assigned to a variable. The array will contain nils if the values doesn't match - that's why I put #compact at the end.
Heres an easy way to do find the keys of a given value:
clients = {
"yellow"=>{"client_id"=>"2178"},
"orange"=>{"client_id"=>"2180"},
"red"=>{"client_id"=>"2179"},
"blue"=>{"client_id"=>"2181"}
}
p clients.rassoc("client_id"=>"2180")
...and to find the value of a given key:
p clients.assoc("orange")
it will give you the key-value pair.

Resources