Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confused about the million ways to reach inside Ruby Hash with the symbol sign

Tags:

ruby

hash

I am drowning in the million ways to access Hash.

condition1 = {
    "e" =>1
}

condition2 = {
    "e":1
}

condition3 = {
    :"e" =>3
}


condition4 = {
    e:4
}


condition5 = {  # 100% sure not working because of syntax error
    e=>5
}

condition6 = {  # 100% sure not working because of syntax error
    :e:6
}  

condition7 = {
    :e=>7
}

puts condition1["e"]
puts condition2["e"]
puts condition3[:e]
puts condition4[:e]
puts condition7[:e]

Output:

1
                                     (nothing)
3
4
7

First question: For the first 2 puts statement, why is it working for the first one but not the second one? In both hashes, I am using Strings as keys. Is it because I used "=>" in the first Hash and thus the string is actually being read as Symbols? So it is actually being processed like :"e" =>1?

Second question: That leads to my second question about the third condition, I was told that if I used Symbol sign before a quote, what happens in the background is that the quote will be ignored. That's why I was able to access it through simply :e without the quotes. Is that true? However if that's true, then the first question becomes more confusing. If in the first condition, "e"=>1 was evaluated as :"e" =>1, then shouldn't the quotes be ignored? Thus eventually it's read as :e =>1. Then how come I was able to access it with only the quotes?

Third question: So how do I access the second condition? I know it's not recommended to use strings as key values. However what if in a certain case I have already created it with strings, how do I access it? I have tried accessing it through a variable name but it's still not outputing anything.

Thank you so much for reading this. I know it's definitely not worth it to care too much about these details. But I ran into it today and can't really move on without figuring out what the HECK is going on !!

like image 700
whales Avatar asked Jun 11 '16 02:06

whales


People also ask

What is hash and symbol in Ruby?

In Ruby, symbols are immutable names primarily used as hash keys or for referencing method names. Hashes and Symbols. Lesson 1 of 2. The Story So Far. Recall that hashes are collections of key-value pairs, where a unique key is associate…

How do you access the hash in Ruby?

In Ruby, the values in a hash can be accessed using bracket notation. After the hash name, type the key in square brackets in order to access the value.

Can hashes have symbols?

Symbols are most commonly used as placeholders in Hashes. We have used symbols already in Rails, for example the Rails params hash associates any the values of any parameters passed in by the user (from a form or in the url) with a symbol representing the name of that value.

How do you write a hash in Ruby?

In Ruby you can create a Hash by assigning a key to a value with => , separate these key/value pairs with commas, and enclose the whole thing with curly braces.


2 Answers

Rule of thumb

If there's a colon (:) it's a Symbol. If there's a hashrocket (=>), it's whatever is to the left of the hashrocket (which can be anything).

Declaring keys in a Hash literal

When we say "Hash literal" we mean code that declares a Hash with curly braces ({ foo: 1 }) or as method arguments (bar(baz: 2)).

There are two ways to declare a key in a Hash literal.

Keys with hashrockets (=>)

The first way to declare a key is with the hashrocket (=>). When you use the hashrocket, the key is whatever value is to the left of it, and you can put any kind of object (or expression) to the left of it:

hashrocket_hash = {
  "I am a String" => 1,
  :I_am_a_Symbol => 2,
  :"I am also a Symbol" => 4,
  /I am a Regexp!/ => 5,
  Kernel => 6,
  if true then "I am also a String" end => 7,
  nil => 8
}

p hashrocket_hash.keys
# => [ "I am a String",
#      :I_am_a_Symbol,
#      :"I am also a Symbol",
#      /I am a Regexp!/,
#      Kernel,
#      "I am also a String",
#      nil
#    ]

p hashrocket_keys.map(&:class)
# => [ String,
#      Symbol,
#      Symbol,
#      Regexp,
#      Module,
#      String,
#      NilClass
#    ]

Keys with colons (:)

The other way to declare a key is with a colon (:). When you use a colon the resulting key is always a Symbol. The usual Symbol rules apply (read the very thorough answer here: What can a ruby symbol (syntax) contain?) except the colon goes at the end instead of the beginning:

colon_hash = {
  I_am_a_Symbol: 9,
  "I am also a Symbol": 10
}

p colon_hash.keys
# => [ :I_am_a_Symbol,
#      :"I am also a Symbol" ]

p colon_hash.keys.map(&:class)
# => [ Symbol,
#      Symbol ]

Accessing a Hash value

There are no special rules for accessing a Hash value. If you want to access a value whose key is a Symbol, you must use a Symbol. If you want to access a value whose key is a String, you must use a String. If the key is something else, you must use that thing.

In the below examples, pay close attention to which keys are followed by colons and which are followed by hashrockets:

hsh1 = { foo: 1 }
p hsh1[:foo]   # => 1
p hsh1[:"foo"] # => 1
p hsh1["foo"]  # => nil

hsh2 = { "bar": 2 }
p hsh2[:bar]   # => 2
p hsh2[:"bar"] # => 2
p hsh2["bar"]  # => nil

hsh3 = {
  Kernel: 3,
  Kernel => 4
}
p hsh3[:Kernel]  # => 3
p hsh3[Kernel]   # => 4
p hsh3["Kernel"] # => nil
like image 109
Jordan Running Avatar answered Sep 29 '22 09:09

Jordan Running


Your confusion is caused by 2 causes, first, the syntax of symbol literal, and the second, the syntax of hash literal.

The symbol literals

The most basic form of a symbol is :hello_world.

However, in order to allow symbol literals contain special characters, ruby introduces this syntax: :'hello-world' or :"hello-world".

The hash literals

Unlike JSON objects in javascript which only allows strings to be the keys, ruby allows anything to serve as a hash key, so the basic form of a hash is

{
  key1 => value1,
  key2 => value2,
  ...
}

Notice that key1, key2 and value1, value2 are just placeholders. You can replace them with anything, which can be:

  • literals
  • variables
  • constants (including classes and modules)
  • method calls

So when using symbol literals as keys and Fixnum literals as values, the basic form is

{:e => 1}

or

{:"e" => 1}

or

{:'e' => 1}

Because symbol literals are often used as hash keys (because they are immutable), Ruby introduced a shorthand version of this specific case: moving the : to the tail of the key, and omit the =>.

{e: 1}

or

{'e': 1}

or

{"e": 1}

Note that there must not be any spaces between e and :, otherwise there is a syntax error.

like image 39
Aetherus Avatar answered Sep 29 '22 10:09

Aetherus