Why {{}} is syntax error while [[]] isn't? [closed] - ruby

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I am trying to understand what's wrong with {{}} while [[]] works.
#works fine
a = []
a = [[]]
h = {}
h = {{}} #throws syntax error, unexpected '}', expecting =>

Because [[]] is array of arrays, while {{}} is meaningless garbage.

Because an hash contains a mapping between a key and a value, when an array contains only a list of values.
Consider {} an hash. In itself, it only is a value.
So you could definitely do :
{ a: {} }
But
{ {} }
in itself is not enough.
Also note that
[ {} ]
obviously works.

You create a hash by providing zero or more key-value pairs.
{} creates a hash with a zero key-value pairs, i.e. an empty hash
{'foo' => 'bar'} creates a hash with a single pair (key 'foo' and value 'bar')
{'foo'} raises a SyntaxError because there's only a key, the value is missing
The error message says the same: unexpected '}', expecting =>, i.e. Ruby expects a => after 'foo', not a closing }.
{{}} raises the same SyntaxError because the value is missing. The hash equivalent to [[]] would probably look like this:
{{}=>{}}

An array needs only value as the member.
So, in the case of this:
a = [[]]
the member is [] which is an empty array.
On the other hand, a hash needs a key and a value to make a member.
So, the formula for making a hash is:
h = { a key => a value, ...}
Each member of a hash is separated by comma.
Therefore if you want to make a valid hash that contains an empty hash inside it.
You can choose one of these:
h = { :key => {} }
or
h = { {} => :value }

Related

Ruby hash defaults: where do nested values go? [duplicate]

This question already has answers here:
Strange, unexpected behavior (disappearing/changing values) when using Hash default value, e.g. Hash.new([])
(4 answers)
Closed 6 years ago.
I wanted to use Ruby's default hash values to allow me to more easily nest hashes without having to manually initialize them. I thought it'd be nice to be able to dig a level down for each key safely without having pre-set the key as a hash. However, I find that when I do this, the data gets stored somewhere, but is not visible by accessing the top-level hash. Where does it go, and how does this work?
top = Hash.new({}) #=> {}
top[:first][:thing] = "hello" #=> "hello"
top[:second] = {thing: "world"} #=> {:thing => "world"}
top #=> {:second => {:thing => "world"}}
top[:first] #=> {:thing => "hello"}
You want to know where your inserted hash is? Maybe you have heard about Schroedingers cat:
h = Hash.new({})
h[:box][:cat] = "Miau"
=> "Miau"
h
=> {}
The cat seem to be dead....
h[:schroedingers][:cat]
=> "Miau"
The cat seem still to be alive, but in a different reality....
Ok, if nothing helps, "Read The Fine Manual". For Hash.new, we read:
If obj is specified, this single object will be used for all default values.
So when you write h[:box], a object is returned, and this object is another hash, and it happen to empty.
Into this empty hash, you write an key-value.
Now this other hash is no longer empty, it has a key-value pair. And it is returned every time you search for a key is not found in your original hash.
You can access the default value via a variety of #default methods
http://ruby-doc.org/core-2.2.3/Hash.html#method-i-default
top.default
=> {:thing=>"hello"}
You can also tell it how you want it to act, example:
irb(main):058:0> top = Hash.new {|h,k| h[k] = {}; h[k]}
=> {}
irb(main):059:0> top[:first][:thing] = "hello"
=> "hello"
irb(main):060:0> top[:second] = {thing: "world"}
=> {:thing=>"world"}
irb(main):061:0> top
=> {:first=>{:thing=>"hello"}, :second=>{:thing=>"world"}}

Using nested hash and a hash as default value isn't working as expected [duplicate]

This question already has answers here:
Strange, unexpected behavior (disappearing/changing values) when using Hash default value, e.g. Hash.new([])
(4 answers)
Closed 6 years ago.
I want to understand why a in the following is an empty hash after a[:a] is called, which is not empty:
a = Hash.new({a:1}) # => {}
a[:a][:a] += 1 # => 2
a # => {}
a[:a] # => {:a=>2}
I know that {a: 1} is shared between every inexistent key (a[:something_inexistent]). But why isn't it displayed as a key of a? If possible, using Ruby's internal would be much appreciated.
It is because a[:a] is never assigned. When you do a[:a], it is not returning the stored value, but is returning the default value.
Unlike this, when you instead have:
a = Hash.new{|h, k| h[k] = {a: 1}}
then, whenever a missing key is called, the block is executed, which assigns the value to the key, and a will no longer be an empty hash.

Ruby syntax confusion [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
I came across this method definition:
def stock_folder_map
res = {}
folders.each { |ff|
ff.stocks.each { |s|
res[s["id"]] = ff["name"]
}
}
end
Can anybody tell what res = {} and res[s["id"]] = ff["name"] mean?
You are building up a new hash named res. You are going through every stock in every folder, and creating a new element in res where the key is the id of the stock, and the value is the name of the folder.
res = {} is assigning to the variable res a new, empty Hash object.
res[s["id"]] = ff["name"] is assigning to whatever key s["id"] evaluates to, a value, retrieved from another hash, ff.
So if for example [s["id"]] evaluates to :foo and ff["name"] evaluates to "bar", the above is equal to doing:
res[:foo] = "bar"
Now those [] are literals for accessing the hash by providing a key. In this case the keys are:
s["id"] (which is another hash) for the res hash and
"name" for the ff hash.
s is most propably another hash.
res = {} declares a local variable res and assigns an empty hash to it. Hashes are indexed using [] which, in Ruby terminology, means that hash instances respond to the [] message and yield some value corresponding to the key value passed to [].
Meanwhile folders.each yields each element of the folders collection in turn to the loop body, assigning each element in turn to the loop variable ff. Given its usage, ff must respond both to stocks and []. One possible way for ff to do this would be for it be an instance of a class such as ClassOfFF outlined below:
class ClassOfFF
def stocks
# Yield a collection (implementing Enumerable, for example)
end
def [](key)
# Yield the element at "key"
end
end
Each object from the collection returned by stocks must itself respond to []. These objects might also be hashes or instances of another class that explicitly responds to [] like ClassOfFF, for example.
The statement res[s["id"]] = ff["name"] invokes [] on both ff and ss and assigns the value of ff["name"] to the element in res with key s["id"].
res = {} is an Hash object.res[s["id"]] is an Hash of Hash.ff["name"] is another Hash.res[s["id"]] = ff["name"] means you are putting the value of the Hash ff at key "name",to the value (which is a key of res) of the Hash s at key "id".
Some pseudocode that might help.
def stock_folder_map
res = {} # creates a new hash map named 'res'
folders.each { |ff| # for each folder in folders (ff = folder)
ff.stocks.each { |s| # for each stock in ff.stocks (s = stock)
# we see that s is a hash map
res[s["id"]] = ff["name"] # let id = s["id"] i.e. s.get("id") in java
# let name = ff["name"] i.e. ff.get("name") in java
# assign value in res i.e. res.put(id, name) in java
}
}
end
The documentation for Hashes might be useful.

Ruby hash of arrays, retrieving values

I have been all over the interwebs and cannot seem to find out how to access an array within a hash. Plenty of ways to iterate through as well as flatten but no plain access.
hash = Hash.new()
data1 = "foo"
data2 = "bar"
hash["foobar"] = {data1, data2}
This all works, now how can I access data1 and data2 individually within the hash?
I was thinking puts hash["foobar"][0] should output foo but it returns nil.
Note: Every array in the hash will have Exactly 2 elements.
I know the answer is a simple 1 liner, I just cannot seem to find it.
As I commented on the question, array literals are square brackets [ ], not curly braces { }. Change your last line to:
hash["foobar"] = [data1, data2]
(You were getting nil presumably because the hash literal had no 0 key. Testing here reveals that , can apparently function as => (*shudder*), so your iteral was equivalent to {data1 => data2}.)

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

Resources