Getting started with Ruby – Part 4 – Hashes & Blocks

In my previous article Getting started with Ruby – Part 3 I started with Collections and covered basics of Ranges & Arrays. Like I mentioned towards the end of the article (in case you couldn’t manage to reach the end) I will start with one of the most commonly used Collection objects in Ruby, namely Hashes.

Hashes are essentially data structures to store key – value pairs, when you wish to store an element in the Hash you do so by assigning a unique key to it within the hash and while retrieving you do so with the help of that unique key. The beauty of hashes is the fact that the ‘key’ in question here could be just about any object in Ruby, most commonly however in practice strings or symbols (i’ll get to what these are soon) are used as keys.

Well i’ve said enough, time for some code :

hash = {} # => {}
hash = Hash.new # => {}
hash = {
    'key1' => 'value1',
    'key2' => Object.new,
    :key3 => Hash.new,
    1 => 2
}

hash['key1'] # => 'value1'
hash['key2'] # => #<Object:0x00000001f090f0>
hash['key3'] # => nil
hash[:key3]  # => {}
hash[1]      # => 2

hash = Hash.new('Doesn\'t Exist!')

hash['dummy-key'] # => "Doesn't Exist!"
# The constructor for a Hash takes the default value as input that should be
# returned when referencing a non-existent key, when nothing is input to the
# constructor the default value is nil

hash['dummy-key'] = 'Dummy Value' # => 'Dummy Value'
hash                              # => {"dummy-key" => "Dummy Value"}
# In order to define a new key-value pair for a hash, you simply use the
# assignment operation for the key, if the key doesn't already exist within
# the array, it creates a new key value pair based on what you assigned,
# however if they key had already existed in the hash, then the value of that
# key would get overwritten by the new assignment

hash.store('key', 'value') # => 'value'
hash                       # => {'dummy-key' => 'Dummy Value', 'key' => 'value'}
# As you can see, you can alternatively use the store method to achieve the
# same, however in practice one rarely use it

hash.has_key?('key')     # => true
hash.has_value?('value') # => true
hash.empty?              # => false
hash.delete('key')       # => 'value'
hash                     # => {'dummy-key' => 'Dummy Value'}
hash.delete('test')      # => "Doesn't Exist!"
hash.clear               # => {}
hash.empty?              # => true
hash.keys                # => ['dummy-key']
# The keys method for a hash returns an array of all the keys available by the
# hash object

hash.values # => ['Dummy Value']
# The values method for a hash returns an array of all the values available in
# the hash object

That just about covers the basics of Hashes, which in turn covers the basics of various built-in data type classes in Ruby.

As per my previous article Getting started with Ruby – Part 3 I have to now get started with loops in Ruby. But before I get into that, I feel it is important that I introduce you to blocks. Blocks in Ruby are essentially nameless functions, in Ruby blocks are used extensively to achieve a lot of cool stuff.

def block_test
    yield if block_given?
end

block_test { puts "test" }
# Here we call the block_test method and pass in a block. A block as
# I explained before is a nameless function, and hence we can do everything
# within a block, that we can do within a function. A block can be defined
# in 2 ways.

{ puts "test1"; puts "test2" }

# OR

do
    puts "test1"
    puts "test2"
end
# As you can see, the first way to define a block is using the curly braces
# {}, which is used to define a block in a single line of code. If you wish to
# still include multiple lines of code within the {} block, you may do so by
# using the ';' which you can use as a delimiter to have multiple Ruby
# statements on a single line. The other way to define blocks is using the
# 'do...end' which allows you to write your code in separate lines and comes in
# more useful when dealing with more complex code.

# In Ruby, every function can optionally receive a single block, which can be
# executed from within the function with the help of the "yield" function. Since
# it is optional, Ruby also provides a special method "block_given?" which
# returns true | false depending on weather a block was passed to the function
# during its call or not.

def block_with_args_test
    yield("string argument1", "string argument2") if block_given?
end

block_with_args_test {|arg1, arg2| puts arg1; puts arg2}
# Output :
# string argument1
# string argument2

# Here you see how you can define blocks that accept arguments. When you use
# the yield method, you can pass in values as arguments and they are then passed
# onto the block during invocation.

For now I think that good enough to go forward, later on we should come across more complex & advanced usages of blocks & closures.

An important point about functions, it is not mandatory to use brackets ‘(‘ & ‘)’ while calling a function for passing the arguments unless you’re calling a function within a function call as an argument or when you are passing a block to it

def test(arg1, arg2)
    puts arg1
    puts arg2
    yield if block_given?
end

test 1, 2
# Output :
# 1
# 2

test(1,2)
# Output :
# 1
# 2

test(1,2) {puts 'test'}
# Output :
# 1
# 2
# test

test 1, 2 {puts 'test'}
# syntax error, unexpected '}', expecting $end
test 1, 2, {puts 'test'}
# syntax error, unexpected '}', expecting $end ```

Considering that the article is already quite long and loaded with a whole lot to digest, I will now take up loops in my next part. I hope the article is easy to understand. If you have any questions or suggestions, please feel free to put them in the comments below and I will make sure I address it asap.

comments powered by Disqus