I'm creating a hash with an array as key, e.g.:
versions = { [1, 2, 3] => 'some-code-name' }
I would like to do the same but using the new hash syntax, which raises an error as of now:
versions = { [1, 2, 3] : 'some-code-name' }
# => SyntaxError: (irb):18: syntax error, unexpected ':', expecting =>
I would like to know if they will implement it in the future (just because it is syntactic sugar).
There's an answer explaining that:
This syntax is only for Ruby 'symbols', and is an alternative to the common usage:
:symbol => 5
rather than as a general key. More on symbols here. And others have written about this with respect to the principal of least surprise (see here).
But I have been able to do the same with a string and thought that maybe they would extend this functionality to be globally available within hashes.
But I have been able to do the same with a string ...
I think you are referring to the "name": value syntax.
Let's see:
{"foo": 123} #=> {:foo=>123}
Notice what happens to the "string"? It becomes a symbol.
Within a hash literal "foo": value is a shortcut for :"foo" => value and :"foo" is in fact a symbol:
:"foo" #=> :foo
I would like to know if they will implement it in the future (just because syntactic sugar)?
Who knows? But I don't think so.
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"}]
How I can pass the keyword arguments to a call to another method in my code without specifying each of them.
Consider the following method with the new first-class support for kwargs:
def foo(obj, bar: 'a', baz: 'b')
another_method(bar, baz)
end
Consider the old Ruby options hash syntax:
def foo(obj, options = {})
another_method(options)
end
I want to pass the keyword arguments without specifying each of them explicitly as with the case of the old options hash syntax. Is that even possible?
You can use the double-splat operator:
def a(q: '1', w: '2')
p q
p w
end
def b(**options)
a(options)
end
b
# => 1
# => 2
b(q: '3', w: '4')
# => 3
# => 4
Keyword arguments work similarly to your "old Ruby options hash syntax". A hash's keys will substitute as keywords and their values as arguments. This should work fine:
def foo(obj, bar: 'a', baz: 'b')
another_method(bar, baz)
end
arg_hash = { bar: 'alt bar', baz: 'alt baz' }
foo(Object.new, arg_hash)
Some things to be aware of
If your hash keys are strings, such as { 'bar' => 'alt bar', 'baz' => 'alt baz' }, this won't work. You'll get an ArgumentError: wrong number of arguments (2 for 1) error. This is easy enough to fix in Rails by calling .symbolize_keys on the hash. If you're not in Rails, you'll have to convert the keys manually or reimplement that method.
Also, in Rails, a HashWithIndifferentAccess (such as the params hash in a controller), although it has both strings and symbols for keys, can't be used in this way. If you pass params, it will look at the string keys instead and you'll get the same error as above. I'm not sure why this is the case, but my guess it's because the params hash starts with strings and adds symbols so the symbols are secondary and not the default. Total guess on that though.
This question already has answers here:
Is there any difference between the `:key => "value"` and `key: "value"` hash notations?
(5 answers)
Closed 7 years ago.
What is the difference between :limit and limit:? I am assuming the first is the method for limiting entry size in Ruby on Rails. What is the second for? I also don't know what is the significance of putting column n on the right side.
This is probably an issue with hash notation.
Ruby 1.8 and prior use a style like this:
method(:limit => 10)
Ruby 1.9 and more recent have a new style that looks like:
method(limit: 10)
The new notation is a lot more like that in other languages like Python and JavaScript. They are functionally identical, though, as you can check with irb where it always shows in conventional notation:
{ test: 'value' }
# => { :test => 'value' }
As to your question as to what limit means, it really depends on what method you're passing this in to. In the context of a schema definition it can limit the size of a field:
t.string limit: 1024
If it's in the context of a query it might limit the number of results returned. Each method has their own interpretation so you'll need to consult the documentation on each method you encounter.
:limit is a value of type symbol. You can see more about symbols on Ruby docs. http://ruby-doc.org/core-2.2.2/Symbol.html
limit: is a syntactic sugar and can be used only as a hash key when this key is a symbol. Example: { :limit => 10 } is the traditional way. After Ruby 1.9.3 you can rewrite that like { limit: 10 }
Variables that have a colon : before their name denounces that they are symbols (unique identifier) and this means that it is possible to do the following:
symbol = :limit
using the colon after the name is usually meant to indicate a hash key, such as the following:
hash = { limit: 5 }
puts hash[:limit] # returns 5
The confusion often ensues when working with older versions of ruby, where hashes are written as follows:
hash = { :limit => 5 }
puts hash[:limit] # returns 5
Which has the same exact meaning as the above statement.
when looking to some Ruby code I found the following method:
def connection
unless #_mc_connection && valid? && #_ns_version == get_version
#_mc_connection = ::Dalli::Client.new(self.dalli_servers, self.dalli_options.merge(namespace: namespace))
end
#_mc_connection
end
My question is about the use of dalli_options.merge(namespace: namespace). What is the purpose of the colon here? Is an hash member?
What is the purpose of the colon here? Is an hash member?
Yes, it is a Hash object.
A Hash can be easily created by using its implicit form:
grades = { "Jane Doe" => 10, "Jim Doe" => 6 }
Hashes allow an alternate syntax form when your keys are always symbols. Instead of
options = { :font_size => 10, :font_family => "Arial" }
You could write it as:
options = { font_size: 10, font_family: "Arial" }
Depending on the Ruby version, this is either a Hash literal (1.9) or a keyword argument (2.0+).
The colon is part of the symbol syntax.
The following are equivalent:
namespace: #only valid inside a hash
and
:namespace
With the former, the 'hash rocket' operator (=>) can be omitted (and usually is, for ease of reading).
However, this is only the case when your keys are symbols. If your keys are strings, as in
{ 'namespace' => 'api' }
the hash rocket is required.