Display multiple hash keys notation - ruby

Let's say i have a ruby hash in the style of savon soap xml response to hash
hash1= { node1­: {node­2:{node3:1­,node4:2}}­}
now to display this hash
hash1[:nod­e1][:node2­][:node3]
works and outputs => 1
hash1[:nod­e1][:node2­][:node4]
works and output => 2
hash1[:nod­e1][:node2­][:node3][:node4]
gives TypeError
although i have seen that type of code on savon scripts. What doesnt it work in my situation ?

hash1[:nod­e1][:node2­][:node3][:node4] is calling the method [] on
hash1[:nod­e1][:node2­][:node3].
Its the equivalent of trying 1[:node4]. The method on an integer takes a Fixnum and cannot implicitly convert a symbol (or a string etc) into an integer.

These multiply-nested hashes are difficult to read, aren't they? Let's spread your hash out a bit:
hash1= {
node1­: {
node­2: { node3:1­, node4:2 }
}­
}
So: The value of node1 is itself a hash. The only entry in that hash, node2, also has a hash for a value. This hash has two entries: node3 and node4, both of which have integers as values.
So hash1[:node1][:node2] returns {node3:1, node4:2}. And hash1[:node1][:node2][:node3] returns 1.
But hash1[:node1][:node2][:node3][:node4] doesn't make any sense, because 1 isn't a hash, and therefore doesn't have a key :node4. That key belongs to the :node2 hash.
It would make sense if you had hash1= { node1­: {node­2: {node3: {node4:2} }}­}. But you don't.
Like I said: these nested hashes are a pain to read...

Related

Pulling out values from element of array

I have a hash that is generated by IB-ruby and looks like this:
{:contract=>#<IB::Stock:0x0000560721a1aee0 #attributes={:symbol=>"AAPL", :currency=>"USD", :sec_type=>"STK", :created_at=>2019-10-23 23:03:35 +0200, :con_id=>0, :right=>"", :include_expired=>false, :exchange=>"SMART"}>, :last_price=>0.24308e3, :high=>0.24324e3, :low=>0.24122e3, :close_price=>0.23996e3, :open_tick=>0.2421e3, :bid_price=>0.2431e3, :ask_price=>0.24319e3}
How do I pull out the symbol ("AAPL") and the closing_price (0.23996e3) for further processing?
What you posted is the string representation of a Hash.
This Hash has a key :close_price, whose value you can access in this way:
your_hash[:close_price] #=> 0.23996e3
The hash also has a key :contract whose value is an instance of the class IB::Stock. To access this object:
ib_stock_instance = your_hash[:contract]
ib_stock_instance.class #=> IB::Stock

Accessing multiple nested hiera values from puppet code (or "puppet lookup" cmd)

This is probably a very easy question, but it seems to be hard to search for the answer on the internet, or at least I spend too much time with this.
How can I access or directly make a list from all foo::bar values from hieradata file (below) in the Puppet module with lookup() or better way?
---
foo::bar:
'some uniq name':
baz: 12345
...
'another uniq name':
baz: 54321
...
So if it would be possible to use wildcards the key path would look like this -> foo::bar::*::baz.
This requires the use of the lookup function, a lambda iterator, and hash syntax notation, so it actually is not that easy, although the code may make it seem that way.
We need to iterate over the values for the keys inside the foo::bar hash. We can start off with that via:
lookup(foo::bar, Hash).each |String $key, Hash $value| {
# first $key is 'some uniq name' string
# first $value is 'some uniq name' hash
}
Now we need to access the values for the bar key inside each nested hash. We can do that by the normal syntax for accessing values of keys inside a hash:
lookup(foo::bar, Hash).each |String $key, Hash $value| {
$value['baz'] # first value is 12345
}
However, we need to store these values inside a variable so they are retained instead of being discarded after exiting the lambda scope. Therefore, we need to have a variable store the return value of the lambda iterator and use a lambda iterator that returns a modified array:
$bazes = lookup(foo::bar, Hash).map |String $key, Hash $value| {
$value['baz']
}
Thus achieving the goal of storing an array (or list as you put it) of all the baz values inside the hieradata. Although the code is short, it is arguably not that simple.
Helpful Documentation -
lookup: https://puppet.com/docs/puppet/5.2/hiera_use_function.html
lambda iterator map: https://puppet.com/docs/puppet/5.3/function.html#map
accessing hash values: https://puppet.com/docs/puppet/5.3/lang_data_hash.html#accessing-values

Access variable hash depth values with square brackets notation

Given this hash:
hash1= { node1: { node2: { node3: { node4: { node5: 1 } } } } }
We access inside nodes with square brackets like this:
hash1[:node1][:node2][:node3][:node4]
Now I have a hash that I know will always be nested as it is an XML response from a SOAP webservice, but neither the depth of the hash nor the names of the nodes stay the same. So it would be nice if I could ask the user of my application for the hash depth and store it in a variable. And then be able to do hash1[:hash_depth] and achieve the same result as above.
I have accomplished what I want by the following code:
str = 'node1,node2,node3,node4'
str_a = str.split(',')
hash_copy = hash1
str_a.each { |s| hash_copy = hash_copy.[](s.to_sym) }
hash_copy
=> {:node5=>1}
hash1[:node1][:node2][:node3][:node4]
=> {:node5=>1}
that is asking the user to enter the hash depth separated by commas, store it in a string, split it, make an array, clone the original hash, go down each level and modify the hash till I get to the desired node. Is there a way to do it with the square brackets notation and using a variable to store the depth without modifying the hash or needing to clone it?
Edit:
someone answered with the following (can't see his post anymore???)
hash_depth="[:node1][:node2][:node3][:node4]"
eval "hash1#{hash_depth}"
Although eval does everything you need, there is another approach, since you already have the working code for comma-separated list:
hash_depth="[:node1][:node2][:node3][:node4]"
csh = hash_depth.gsub(/\A\[:|\]\[:|\]\Z/, { '][:' => ',' })
#⇒ "node1,node2,node3,node4"
And now you are free to apply your existing function to csh.
If this is a webapp, I think you should prepare a list of short textareas, which starts with a single text item, and the user can keep adding a new item to the list by clicking on a button. The areas will be filled by the user, and will be sent.
Then, you will probably receive this through some serialized form. You decode this to get an array of strings:
str_a = ["node1", "node2", "node3", "node4"]
and you can reach the inner element by doing:
str_a.inject(hash1){|h, s| h[s.to_sym]} #=> {:node5 => 1}

how to find a hash key with one of the predefined names?

I have a hash with an arbitrary key:
{'GET': [1,2,3]}
or
{'POST': ['my data 0', 'my data 1']}
The hash is generated from JSON which is sent in the request body. There is just one key, or rather, I ignore any keys but one.
I want to find which key it is, and this is the code that I wrote:
items = data['GET'] || data['get'] || data['POST'] || data['post']
this does not look neat. If the number of keys that I want to process grows the expression will be long. I want it to be short. I am new to Ruby, is there a better way?
If you think it might grow, you may want to separate the HTTP methods from the finding of that method in the data:
methods = [:get, :post]
def find_method(data)
keys = methods.map{|m| [m.to_s.upcase, m.to_s]}.flatten
data.values_at(keys).first
end
You could just get the first value (assuming there's only one) like this:
item = data.values.first
You could use the Hash#values_at method.
http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-values_at
data.values_at('GET','get', 'POST','post').first

How do I extract a value from this Ruby hash?

I'm using the Foursquare API, and I want to extract the "id" value from this hash
[{"id"=>"4fe89779e4b09fd3748d3c5a", "name"=>"Hitcrowd", "contact"=>{"phone"=>"8662012805", "formattedPhone"=>"(866) 201-2805", "twitter"=>"hitcrowd"}, "location"=>{"address"=>"1275 Glenlivet Drive", "crossStreet"=>"Route 100", "lat"=>40.59089895083072, "lng"=>-75.6291255071468, "postalCode"=>"18106", "city"=>"Allentown", "state"=>"Pa", "country"=>"United States", "cc"=>"US"}, "categories"=>[{"id"=>"4bf58dd8d48988d125941735", "name"=>"Tech Startup", "pluralName"=>"Tech Startups", "shortName"=>"Tech Startup", "icon"=>"https://foursquare.com/img/categories/shops/technology.png", "parents"=>["Professional & Other Places", "Offices"], "primary"=>true}], "verified"=>true, "stats"=>{"checkinsCount"=>86, "usersCount"=>4, "tipCount"=>0}, "url"=>"http://www.hitcrowd.com", "likes"=>{"count"=>0, "groups"=>[]}, "beenHere"=>{"count"=>0}, "storeId"=>""}]
When I try to extract it by using ['id'], I get this error can't convert Symbol into Integer. How do I extract the value using ruby? Also, how do I do this for multiple hashes extracting the "id" value each time?
Please pardon my inexperience. Thanks!
It's wrapped in an array, that's what the [ and ] mean on the start and end. But it also looks like this array only one object in it, which is the hash you really want.
So assuming you want the first object in this array:
mydata[0]['id'] # or mydata.first['id'] as Factor Mystic suggests
But usually when an API returns an Array there is a reason (it might return many results instead of just one), and naively plucking the first item from it my not be what you want. So be sure you are getting the kind of data you really expect before hard coding this into your application.
For multiple hashes, if you want to do something with the id (run a procedure of some kind) then
resultsArray.each do |person|
id = person["id"] #then do something with the id
end
If you want to just get an array containing the ids then
resultsArray.map{|person| person["id"]}
# ["4fe89779e4b09fd3748d3c5a", "5df890079e4b09fd3748d3c5a"]
To just grab the one item from the array, see Alex Wayne's answer
To get an array of ids, try: resultsArray.map { |result| result["id"] }

Resources