Introduction to Ruby

Introduction to Ruby

Data Types

A literal is any notation that lets you represent a fixed value in source code. For instance, all of the following are literals in Ruby: Data types like strings,integer literals, array literals,hash literals,symbol literal and nil.

'Hello, world!'          # string literal
375                      # integer literal
3.141528                 # float literal
true                     # boolean literal
{ 'a' => 1, 'b' => 2 }   # hash literal
[ 1, 2, 3 ]              # array literal
:sym                     # symbol literal
nil                      # nil literal

nil means nothing in ruby

Type Conversion

Type conversion means converting from one data type to another is called type conversion,here 13 which is a integer is being converted into a float using to_f which converts the integer ot 13.0,similarly for the string,converting 13 to string using to_s.

irb(main):001:0> 13.to_f
=> 13.0
irb(main):002:0> 13.to_f
=> 13.0
irb(main):003:0> 13.to_s
=> "13"
irb(main):004:0> 

String Interpolation

Often in input output statements,string interpolation is used for variable placeholders within the puts statements.

irb(main):013:0> #String interpolation
=> nil
irb(main):014:0> name= "vinay"
=> "vinay"
irb(main):015:0> puts "hello, #{name}"
hello, vinay
=> nil                                                 

In the above example the variable name is used in the puts statement.

Strings

Strings are group of characters,there are lot of inbuilt function for string operations few of them are, .capitalize capitalizes the first alphabet of the string,empty? returns a boolean whether the string is empty or not,length returns the length of the string,split function splits the characters in the string based on the separator.

irb(main):016:0> "Vinay".capitalize
=> "Vinay"
irb(main):017:0> "vinay".capitalize
=> "Vinay"
irb(main):018:0> "hello".empty?
=> false
irb(main):019:0> "vinay".length
=> 5
irb(main):020:0> "hello world".split
=> ["hello", "world"]
irb(main):021:0> "hello world".split("")
=> ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"]

Basic Data Structures Arrays and Hashes.

Array Literals and Hash literals are important data structures,to handle large continous data. Array is a collection of data of the same type,that are accessed by index,whereas Hash are stored in a key:value pair,where the value is accessed by mentioning the key.

irb(main):022:0> #Basic data strctures arrays and hashes
=> nil
irb(main):023:0> [1,4,5,67,8]
=> [1, 4, 5, 67, 8]
irb(main):024:0> #hashes
=> nil
irb(main):025:0> {:dog => "barks"}
=> {:dog=>"barks"}
irb(main):026:0> 

Symbols

Symbol is a text that begins with a colon(:symbol) while strings are enclosed within the double quotes.Strings are mutable objects,while symbols are immutable objects,since strings are mutable and their value can be changed,it can cause bugs, there at that time where object’s value shouldn’t be changed,at that time symbols can be used.

irb(main):021:0> "a string".object_id
=> 237820 irb(main):022:0> "a string".object_id
=> 239900
irb(main):023:0> "a string".object_id
=> 241980
irb(main):024:0> :a_symbol.object_id
=> 2332828
irb(main):025:0> :a_symbol.object_id
=> 2332828
irb(main):026:0> :a_symbol.object_id
=> 2332828

For every new string created there is a different object_id created,making it different objects,where as in symbols,it points to the same object id.A

Variables

Variable names are reusable,you can assign a new value to a variable at any point.To name variables in ruby follow snake_case and not CamelCase

age=20

Variables as references,variable is effectively a reference or a pointer to that address in memory.

desired_location= "Bengaluru"
referenced_location= desired_location

reference_location.upcase!

here since the referenced_location is a pointer to desired_location changing the referenced_location will also reflect on the actual variable.

  • gets and chomp
irb(main):034:0> name = gets
Bob
=> "Bob\n"                                  
irb(main):035:0> name = gets.chomp
Bob
=> "Bob"                                                                         
irb(main):036:0> "hello "+ name
=> "hello Bob"
irb(main):037:0> 

Variable Scope variable’s scope determines where in a program the particular variable is accessible,

name="Vinay Keshava"
def print_full_name(first_name,last_name)
  name = first_name+ ' ' + last_name
  puts name
end

print_full_name 'Mahesh', 'Kumar'
print_full_name 'Suresh', 'Kumar'
puts name

Output of the above program.

$ ruby variable_examples.rb 
Mahesh Kumar
Suresh Kumar
Vinay Keshava
  • Variable blocks
total=0
[1,2,4].each { |number| total += number}
puts total

a=5
3.times do |n|
  a=3
end
puts a
  • Types of Variable

    • Constants
    • Global Variablesruby
    • Class Variables
    • Instance Variables
    • Local Variables
  • Constants

Capitalizing every letter in variable’s name.Constants are used for storing data that never needs to change.

MY_CONSTANT = 'I am available throughout your app.'
  • Global Variables Accessible throughout the entire app,overriding all scope boundaries.
$var = 'I am also available throughout your app.'
  • Class Variables

Class Variables are declared by starting the variable name with two @ signs,these variables are accessible by instances of the class and by the class itself.

@@instances = 0
  • Instance Variables Instances variables are declared by starting the variable with one @ sign,these variables are available throughout the current instance of the parent class.
@var = "I'm available throughout the current instance of this class"
  • Local Variable

Most common variables you will come across and obey all scope boundary rules.

var = 'Hello world'

Input & Output

gets:returns the user input,difference bw puts & print is puts appends a new line at the end of the string while print does not.

irb(main):001:0> puts "hello world"
hello world
=> nil                                           
irb(main):002:0> print "hello world"
hello world=> nil
irb(main):003:0> gets
hello world
=> "hello world\n"                               
irb(main):004:0> gets.chomp
hello world
=> "hello world"                                                          
irb(main):005:0> name =gets.chomp
hell world
=> "hell world"                                                               
irb(main):006:0> name
=> "hell world"

since gets appends an new line escape character at the end of the line,to avoid this we use chomp.

Conditional Logic

Conditional statements,similar to in other programming languages,case statements just like switch checks for condition against a set of values defined,where in the example below,is demonstrated using the grade variable with the help of case and when keywords.

if true==false
  print "This is not printing"
elsif true== true
  print "True is true hence false is false\n"
end

if 1<2 && 5<6
  print "This is printing because 1 is smaller than 2\n"
end

#case statements
grade = 'F'
pass_fail = case grade
            when 'A' then "Hell yeah distinction"
            when 'B' then "Ahh first class yes yes"
            else "You shall not pass in life"
end
puts pass_fail


percentage = 50.00
pass_fail_percentage = case percentage
                       when 70.00
                         puts "Above 70.00 you dumbshit"
                         total=percentage*100
                       when 80.00
                         puts "80.00 ahh yes yes"
                         total=percentage*100
                        else
                          puts "You shall not pass go graze donkeysss man"
                        end

# unless statements - unless statement works in the opposite way to if statement,it only processes the code if it evaluates to false,unless can be added with else too.
age = 19
unless age <18
  puts "get a job"
else 
  puts " Careful now"
end

# Ternary Operator
response = age < 18? "You still have time to become 19": "You're a grown up kid"
puts response

unless statement works in the opposite way of if statement,it processes the block of code only if it evaluates to false.

Loops

Loops are used to repeat a set of statements.Types of loops are loop,for,while. until loop opposite to while loop,which evaluates as long as the condition is false not like while loop which only evaluates when the expression evaluate to true.

i=0
#this type of ruby's loop is infinite until you stop it.
loop do
  puts "i is #{i}"
  i+=1
  break if i==10
end

#while loop
i=0
puts "while loop"
while i<10 do
  puts "i is #{i}"
  i+=1
end

#using while loop with gets
while gets.chomp !="yes" do
  puts "Are we there yet?"
end

#until loop
i=0
until i>=10 do
  puts "i is #{i}"
  i +=1
end

# forloops
for i in 0..5
  puts "#{i} zombie coming"
end

#times loop
5.times do
  puts "hello world"
end

#upto and downto loops
5.upto(10) { |num| print "#{num}" }
puts " "
10.downto(5) { |num| print "#{num}" }

#iterators
names = ["vinay","keshava","hello","world"]
names.each { |name| puts name }

Block is some lines of code that is ready to be executed,when working with blocks they are two styles,single line and multi line. In single line block we use {} curly braces,while in multi line style we use the do and end keywords.

##block
names = ["vinay","keshava","hello","world"]
names.each { |name| puts name }

names.each do |name|
  puts "#{name}"
end

Arrays

Arrays are a collection of data of the same type,that are referenced by an index.Array.new is another way to create an array apart from the traditional way of creating it. Array elements are accessed by an index. some of the important methods used in arrays are first, which returns the value at the first index,and last function returns the value at the last index.

num_array=[1,3,4,5,76]
str_array = ["vinay","hello","world","keshava"]

#Creating an array using .new
Array.new
Array.new(3) #creates a new array with size 3
a1 = Array.new(4,7) #creates a new array with size 3,with all the values in these indexes is "7"
puts a1
Array.new(4,true)


#first and last array methods - to access the first and last value of the array.
puts str_array.first
puts str_array.last
puts str_array.first(3)

# adding a element to the array.
num_array=[1,2]
print num_array
puts ""

num_array.push(3,4)
print num_array
puts ""
num_array << 5 # shovel operator which adds 5 to the array.
print num_array

# deleting an element from the array.
num_array.pop     # delete 5
puts num_array

Shift/Unshift and Push/Pop

.unshift and .push is used to add an element to an array, while `.unshift’ adds element to beginning of the array,while ‘.push’ adds the element to the end.

.shift and .pop is used to remove an element from the array,and the removed element is returned,.shiftremoves an element from the beginning while, .pop removes the element from the end.

#shift unshift and push and pop
=> [1, 1, 3, 4, 5]
irb(main):005:0> num_array
=> [1, 1, 3, 4, 5]
irb(main):006:0> num_array.shift
=> 1
irb(main):007:0> num_array
=> [1, 3, 4, 5]
irb(main):008:0> num_array.push(10)
=> [1, 3, 4, 5, 10]
irb(main):010:0> num_array.unshift
=> [1, 3, 4, 5, 10]
irb(main):011:0> num_array.unshift(1)
=> [1, 1, 3, 4, 5, 10]
irb(main):012:0> num_array.shift
=> 1
irb(main):013:0> num_array
=> [1, 3, 4, 5, 10]
irb(main):014:0> num_array.shift
=> 1
irb(main):015:0> num_array
=> [3, 4, 5, 10]
irb(main):016:0> num_array.shift
=> 3
irb(main):017:0> num_array
=> [4, 5, 10]
irb(main):018:0> 

Basic array methods

num_array.methods in the irb will display all the array methods available.

Hashes

One of the drawback of Array’s is,it holds data of the same type and the way of accessing the elements in the array is time consuming due to indexing of elements from 0. Hash is like a key:value pair,where to access the value,we use the key which can be of different data types,can be string,array,integer etc.

Another style of creating an hash similar to array using .new like Hash.new.

To access the values in the hash we use the fetch option along with key.

Stings are not prefferred to use as keys in the hash due to their mutable nature,hence symbols are used due to their immutable nature.

irb(main):004:0> hash_with_different_types = { "name"=> "vinay", "degrees"=>["MBA","MTECH","MS"],12=>"twelve" }
=> {"name"=>"vinay", "degrees"=>["MBA", "MTECH", "MS"], 12=>"twelve"}
irb(main):005:0> #creating a hash with Hash.new
=> nil
irb(main):006:0> new_hash=Hash.new
=> {}
irb(main):007:0> new_hash[10=>"ten"]
=> nil
irb(main):008:0> new_hash
=> {}
irb(main):009:0> new_hash={10=>"ten"}
=> {10=>"ten"}
irb(main):010:0> #accessing hash values
=> nil
irb(main):011:0> new_hash[10]
=> "ten"
irb(main):012:0> new_hash.fetch(10)
=> "ten"
irb(main):013:0> #adding and changing data
=> nil
irb(main):014:0> my_hash_example[8]="eighttttee"
=> "eighttttee"
irb(main):015:0> my_hash_example
=> {10=>"ten", 8=>"eighttttee", 7=>"seven"}
irb(main):016:0> my_hash_example[9]="ninie"
=> "ninie"
irb(main):017:0> my_hash_example
=> {10=>"ten", 8=>"eighttttee", 7=>"seven", 9=>"ninie"}
irb(main):018:0> #deleting a hash data element
=> nil
irb(main):019:0> my_hash_example.delete(10)
=> "ten"
irb(main):020:0> my_hash_example
=> {8=>"eighttttee", 7=>"seven", 9=>"ninie"}

irb(main):021:0> #methods in hashes
=> nil
irb(main):022:0> my_hash_example.keys
=> [8, 7, 9]
irb(main):024:0> my_hash_example.values
=> ["eighttttee", "seven", "ninie"]

irb(main):027:0> #merging two hashes
=> nil
irb(main):028:0> my_hash_example.merge(hash_with_different_types)
=> {8=>"eighttttee", 7=>"seven", 9=>"ninie", "name"=>"vinay", "degrees"=>["MBA", "MTECH", "MS"], 12=>"twelve"}
irb(main):029:0> new_merged_hash=my_hash_example.merge(hash_with_different_types)
=> {8=>"eighttttee", 7=>"seven", 9=>"ninie", "name"=>"vinay", "degrees"=>["MBA", "MTECH", "MS"], 12=>"twelve"}
irb(main):030:0> new_merged_hash
=> {8=>"eighttttee", 7=>"seven", 9=>"ninie", "name"=>"vinay", "degrees"=>["MBA", "MTECH", "MS"], 12=>"twelve"}
irb(main):031:0> 

Symbols

The first style is rocket style of declaring hashes,while the second style is symbols syntax

irb(main):031:0> #symbols as hashes
=> nil
irb(main):032:1* american_cars = {
irb(main):033:1*   :chevrolet => "Corvette",
irb(main):034:1*   :fort => "Mustang",
irb(main):035:1*   :ferrari => "1"
irb(main):036:0> }
=> {:chevrolet=>"Corvette", :fort=>"Mustang", :ferrari=>"1"}
irb(main):037:0> american_cars
=> {:chevrolet=>"Corvette", :fort=>"Mustang", :ferrari=>"1"}
irb(main):038:1* japnese_cars = {
irb(main):039:1*   honda: "Accord",
irb(main):040:1*   toyota: "Corolla",
irb(main):041:1*   nissan: "Altime"
irb(main):042:0> }
=> {:honda=>"Accord", :toyota=>"Corolla", :nissan=>"Altime"}

Methods

Methods are useful, when you want to repeatedly want to execute a set of statements based on condition,then comes methods into picture.

def hello_world_method
  puts "hello world"
end
hello_world_method

def add_two_numbers(a,b)
  puts a+b
  #puts a-b Error: Undefined method
end
add_two_numbers(10,20)
add_two_numbers("hello","world")


#method with return type and argument
def hello_world_with_argument(name)
  return name+" Hello World"
end
puts hello_world_with_argument("vinay")


#default parameters
def method_with_default_parameters(name="Vinay")
  "Hello" + name
end
puts method_with_default_parameters
puts method_with_default_parameters("Mahesh")


#predicate methods- which return a boolean
puts 5.even?
puts 6.even?

Advanced Arrays

puts "Remove all duplicates"
array1 = [1,1,1,1,2]
print array1.uniq

puts "\nRemoving All Nil Elements"
array1=[1,2,4,5,nil,"John"]
array2=array1.compact
#compact removes all the nil elements.
print array1.to_s
print array2.to_s

puts "Remove Odd numbers"
array=[10,34,44,32,21,9,3,8]
even_array= array.reject do |item|
  item.odd?
end
puts even_array


puts "Joining Arrays"
#using concat
numbers1= [1,2,4,5]
numbers2= [5,6]

all_numbers = numbers1+ numbers2
print all_numbers

# Destructuring Array.- Instead of manually assigning values using indexes, we can use this option of destructuring arrays.
puts "\nDestructuring Array"
destructuring_array = [89,90,"Hello","world"]
mark1,mark2,string1,string2 = destructuring_array
puts mark1
puts mark2
puts string2

# Destructuring using greedy variables- adding a star before a variable name makes the variable greedy and that takes all the remaining values
mark1,*other_values= destructuring_array
puts "Destructuring array using greedy variable"
puts mark1
print other_values

puts "\nVariables are greedy,but ruby is damn smart"
mark1, *other_variables, string2= destructuring_array
puts mark1
print other_variables
print string2

puts "\n Creating an array using %w"
w_array = %w{one two three four}
print w_array

puts "\n Quicker way to create array of strings"
array_of_strings= %w[hello world blue green red yellow]
print array_of_strings

puts "\n Quicker way to create array of symbols using %i"
array_of_symbols = %i[symbol1 symbol2 symbol3]
print array_of_symbols

Comments

# single line comments
 =begin
 hello world this is a multi line comment in ruby
 =end

Debugging with pry-byebug gem

> ruby advanced_arrays.rb
Remove all duplicates
[1, 2]
Removing All Nil Elements
[1, 2, 4, 5, nil, "John"][1, 2, 4, 5, "John"]Remove Odd numbers
10
34
44
32
8
Joining Arrays
[1, 2, 4, 5, 5, 6]
Destructuring Array

From: /home/vinay/Documents/learn/ruby/ruby-exercises/advanced_arrays/advanced_arrays.rb:34 :

    29: # Destructuring Array.- Instead of manually assigning values using indexes, we can use this option of destructuring arrays.
    30: puts "\nDestructuring Array"
    31: destructuring_array = [89,90,"Hello","world"]
    32: mark1,mark2,string1,string2 = destructuring_array
    33: binding.pry
 => 34: puts mark1
    35: puts mark2
    36: puts string2
    37: 
    38: # Destructuring using greedy variables- adding a star before a variable name makes the variable greedy and that takes all the remaining values
    39: mark1,*other_values= destructuring_array

[1] pry(main)> mark1
=> 89
[2] pry(main)> mark2
=> 90
[3] pry(main)> string1
=> "Hello"
[4] pry(main)> 

Enumerable Methods

puts "Select enumerable methods"
friends = ['Sharon','Vinay','Suresh','Mahesh']
print friends.select { |friend| friend!= 'Vinay'}


puts "\n\nEach enumerable methods"
friends.each { |friend| puts "hello"+ friend}

puts "each_with_index method- returns two block elements,the value and the index of the value"
friends.each_with_index { |fruit, index| print fruit if index.even? }

puts "\n the map method,instead of creating two separate arrays or creating a new array to put the same data,we can use the  map"
friends.each { |friend| friend.upcase }
# the above just converts but doesn't saves to the original array,instead creating a new array will be like duplicating data
friends.map { |friend| friend.upcase}
print friends


puts "\nreduce method- takes an array or hash and reduces it to single object"
my_numbers = [10,20,40,90]
print my_numbers.reduce { |sum, number| sum+number }

puts "\n Bang methods end with an exclamation and often modify the object they are called on"
#just like friends in the above example if we want to modify the original object we use bang method,we use the above map method to modify the original array
friends.map! { |friend| friend.upcase }
print friends

puts "\n Return values of enumerables - it is not a good practice to use bang methods and modify original objects,instead return the enumerables and assign it to a new object"
invited_friends = friends.select { |friend| friend!='VINAY' }
print invited_friends

Predicate Enumerable Methods

#include? predicate enumerable method
numbers = [5,7,7,8]
print numbers.include?(8)

#any method returns true if any elements in array or hash matches the condition within the block;otherwise return false
print numbers.any? { |num| num>10 }
print numbers.any? { |num| num<10 }

#all? method returns true if all the elements in your array or hash matches the condition
puts "\nall? predicate enumerable method"
print numbers.all? {|num| num>1}
# none? method returns true only if the condition in the block matches with none of the elements in array/hash,otherwise it returns false.
print numbers.none? { |num| num>20}
#saying there are no numbers greater than 20