no implicit conversion of nil into String when using hash without => - ruby

my program works fine with this hash
hash =
{
'keyone'=> 'valueone',
'keytwo'=> 'valuetwo',
'keythree'=> 'valuethree'
}
but someone pointed out the this notation is old and that now I should use:
hash =
{
'keyone': 'valueone',
'keytwo': 'valuetwo',
'keythree': 'valuethree'
}
I get this error:
no implicit conversion of nil into String (TypeError)
I only changed the hash notation.
Can someone explain what is happening?

In the latter your keys are saved as symbols. So you should refer to them as:
hash[:keyone]
And if symbols are just fine, this is even better
hash = {
keyone: 'valueone',
keytwo: 'valuetwo',
keythree: 'valuethree'
}
But, if you need string keys, you have to stick with the "old" syntax
hash = {
'keyone' => 'valueone',
'keytwo' => 'valuetwo',
'keythree' => 'valuethree'
}

I only changed the hash notation.
No, you didn't. You also changed the type of the key objects from Strings to Symbols.
{ 'key': 'value' }
is not equivalent to
{ 'key' => 'value' }
it is equivalent to
{ :key => 'value' }

The new notation uses symbols for keys:
hash = {
keyone: 'valueone',
keytwo: 'valuetwo',
keythree: 'valuethree'
}
puts hash
# {:keyone=>"valueone", :keytwo=>"valuetwo", :keythree=>"valuethree"}
Your code also misses the commas between the items.

Related

Ruby: transform Hash-Keys

I have a Hash:
urls = [{'logs' => 'foo'},{'notifications' => 'bar'}]
The goal is to add a prefix to the keys:
urls = [{'example.com/logs' => 'foo'},{'example.com/notifications' => 'bar'}]
My attempt:
urls.map {|e| e.keys.map { |k| "example.com#{k}" }}
Then I get an array with the desired form of the keys but how can I manipulate the original hash?
If you want to "manually" transform the keys, then you can first iterate over your array of hashes, and then over each object (each hash) map their value to a hash where the key is interpolated with "example.com/", and the value remains the same:
urls.flat_map { |hash| hash.map { |key, value| { "example.com/#{key}" => value } } }
# [{"example.com/logs"=>"foo"}, {"example.com/notifications"=>"bar"}]
Notice urls are being "flat-mapped", otherwise you'd get an arrays of arrays containing hash/es.
If you prefer to simplify that, you can use the built-in method for for transforming the keys in a hash that Ruby has; Hash#transform_keys:
urls.map { |url| url.transform_keys { |key| "example.com/#{key}" } }
# [{"example.com/logs"=>"foo"}, {"example.com/notifications"=>"bar"}]
Use transform_keys.
urls = [{'logs' => 'foo'}, {'notifications' => 'bar'}]
urls.map { |hash| hash.transform_keys { |key| "example.com/#{key}" } }
# => [{"example.com/logs"=>"foo"}, {"example.com/notifications"=>"bar"}]
One question: are you best served with an array of hashes here, or would a single hash suit better? For example:
urls = { 'logs' => 'foo', 'notifications' => 'bar' }
Seems a little more sensible a way to store the data. Then, saying you did still need to transform these:
urls.transform_keys { |key| "example.com/#{key}" }
# => {"example.com/logs"=>"foo", "example.com/notifications"=>"bar"}
Or to get from your original array to the hash output:
urls = [{'logs' => 'foo'}, {'notifications' => 'bar'}]
urls.reduce({}, &:merge).transform_keys { |key| "example.com/#{key}" }
# => {"example.com/logs"=>"foo", "example.com/notifications"=>"bar"}
Much easier to work with IMHO :)
If you don't have access to Hash#transform_keys i.e. Ruby < 2.5.5 this should work:
urls.map{ |h| a = h.to_a; { 'example.com/' + a[0][0] => a[0][1] } }

Mongodb replacing dot (.) in key name while inserting document

MongoDb doesn't support keys with dot. I have a Ruby nested hash that has many keys with dot (.) character. Is there a configuration that can be used to specify a character replacement for . like an underscore _ while inserting such data to MongoDb
I'm using MongoDB with Ruby & mongo gem.
example hash is like below
{
"key.1" => {
"second.key" => {
"third.key" => "val"
}
}
}
If it isn't possible to use keys with . in Mongodb, you'll have to modify the input data :
hash = {
'key.1' => {
'second.key' => {
'third.key' => 'val.1',
'fourth.key' => ['val.1', 'val.2']
}
}
}
Transforming string keys
This recursive method transforms the keys of a nested Hash :
def nested_gsub(object, pattern = '.', replace = '_')
if object.is_a? Hash
object.map do |k, v|
[k.to_s.gsub(pattern, replace), nested_gsub(v, pattern, replace)]
end.to_h
else
object
end
end
nested_gsub(hash) returns :
{
"key_1" => {
"second_key" => {
"third_key" => "val.1",
"fourth_key" => [
"val.1",
"val.2"
]
}
}
}
Transforming keys and values
It's possible to add more cases to the previous method :
def nested_gsub(object, pattern = '.', replace = '_')
case object
when Hash
object.map do |k, v|
[k.to_s.gsub(pattern, replace), nested_gsub(v, pattern, replace)]
end.to_h
when Array
object.map { |v| nested_gsub(v, pattern, replace) }
when String
object.gsub(pattern, replace)
else
object
end
end
nested_gsub will now iterate on string values and arrays :
{
"key_1" => {
"second_key" => {
"third_key" => "val_1",
"fourth_key" => [
"val_1",
"val_2"
]
}
}
}
In mongoDB, there is no configuration to support dot in the key. You need to preprocess the JSON before inserting to MongoDB collection.
One approach is that you can replace the dot with its unicode equivalent U+FF0E before insertion.
Hope this helps.

Value inside hash

I have some problems with a hash:
"commissions"=>
{"commission"=>
{"commissionID"=>"38767647",
"date"=>"2014-09-22",
"publisherID"=>"46272",
"domainID"=>"1173659",
"merchantID"=>"35216",
"commissionValue"=>110,
"orderValue"=>2095,
"currency"=>"USD",
"url"=>"http://www.asos.com"},
"commission5"=>
{
other params
}
How can I get the value of 'commissionValue'?
Use [] to get value of the hash by key.
h = {"commissions"=>
{"commission"=>
{"commissionID"=>"38767647",
"date"=>"2014-09-22",
"publisherID"=>"46272",
"domainID"=>"1173659",
"merchantID"=>"35216",
"commissionValue"=>110,
"orderValue"=>2095,
"currency"=>"USD",
"url"=>"http://www.asos.com"},
"commission5"=> { }
}
}
h["commissions"]["commission"]["commissionValue"]
# => 110

Edit a Hash contained in another Hash

I have a Hash inside a Hash with the following structure:
mysystem = {
"slicompany" => {
"sahil" => "developer",
"Jag" => "developer"
},
"uzanto" => {
"kapil" => "tech lead"
}
}
What is the best way to add/delete/edit the elements of the inner Hashes?
As Uri Agassi says it's very easy to access nested hashes via keys...
mysystem['slicompany']['sahil'] = 'tech lead'
For convenience, you can also assign the inner hash to a separate variable, changes to that variable will be reflected in the "outer" variable (since the inner variable and the hash of the outer variable are pointing to the same hash object)
slicompany = mysystem['slicompany']
slicompany["George"] = "dishwasher"
p mysystem
=> {"slicompany" => {"sahil" => "developer", "Jag" => "developer", "George" => "dishwasher"},
"uzanto" => {"kapil" => "tech lead"}}

ruby building hash from url

In Ruby, what is an efficient way to construct a Hash from a request path like:
/1/resource/23/subresource/34
into a hash that looks like this:
{'1' => { 'resource' => { '23' => 'subresource' => { '34' => {} } } }
Thanks
path = "/1/resource/23/subresource/34"
path.scan(/[^\/]+/).inject(hash = {}) { |h,e| h[e] = {} }
hash
=> {"1"=>{"resource"=>{"23"=>{"subresource"=>{"34"=>{}}}}}}
A recursive solution seems like the simplest thing to do. This isn't the prettiest, but it works:
def hashify(string)
k,v = string.gsub(/^\//, '').split('/', 2)
{ k => v.nil? ? {} : hashify(v) }
end
There may be edge cases it doesn't handle correctly (probably are) but it satisfies the example you've given.

Resources