some practice files and notes from Linkedin Learning’s Ruby Essential Training Part 1: The Basics
This assumes knowledge coming from JavaScript
Table of Contents
- use floats for precision calculations
- integer •/÷ integer make less precise (rounded) results
# change to float
num.to_f
# change to integer, truncates like .floor
num.to_i
.round
.floor
.ceil
.abs
- mutable
# append
string << "substr"
# multiplication
string * 3
# type conversion
# number to string
num.to_s
# string to integer
string.to_i
# all caps
string.upcase
# lowercase all
string.downcase
# title case
string.capitalize
# reverse chars
string.reverse
# length
string.length
# tab, newline
"\t \n"
# string interpolation
"some string #{variable}"
# append ( like arr.push )
array << element
# indexing
array[-1] # last element
array[-2] # second til last
etc...
# portion of array
array[starting_i, num_of_elem]
array[starting_i.. ending_i] # range, where starting_i < ending_i
# ! at the end of the methods reassigns the array to the change
array.reverse
array.shuffle
array.flatten
# removes duplicate values
array.uniq
# removes all nils
array.compact
# ? to query or find out about something
array.include?(element_val) # returns true or false
# delete value and index and shifts everything over
array.delete_at(index) # returns the deleted value
# find value and delete if found
array.delete(value) # returns deleted value or nil
# array <-> string (delimiter = optional)
array.join(delimiter)
string.split(delimiter)
# stack methods
array.push
array.pop
array.shift
array.unshift
# add/remove elements
array + [ele1, ele2, ele3]
array - [element]
#find index of value
array.index(value) # returns index or nil
- aka dictionaries, associative arrays
- unordered
- find items by key, not position
hash = {
'key' => 'value',
'label' => 'value'
}
# accessing obj
hash['key'] # returns 'value'
# assigning
hash['label'] = 'another value'
# return array
hash.keys # of keys
hash.values # of values
# converts hash -> array
hash.to_a # returns 2D array of [key, value]
- immutable labels, not variables
- start with
:
- lowercase, underscores
- cannot start key with numbers
- no quotes or spaces
- can be used in hashes
- refers to exact same item
.object_id
in memory, opposed to strings- less frequent garbage collection
hash = {
:some_key => 'Some value',
:a_symbol => 'a value'
}
# retrieve a value
hash[:a_symbol] # returns 'a value'
# shorthand looks like json
hash = {
some_key: 'Some value',
a_symbol: 'a value'
}
# shorthand keys are ALWAYS all symbols
hash.keys.first.class # returns Symbol
- methods names that end in
?
returns booleans
[1, 2, 3].include?(2) # returns true
2.between?(5,8) # returns false
[].empty? # true
hash.has_key?(:some_key) # true
hash.has_value?('value') # false
== < > <= >= ! != && ||
true.class # returns TrueClass
false.class # returns FalseClass
- must wrap range in parens before calling methods if it is not in a variable
- avoid using exclusive ranges
range.class = Range
# inclusive
1..10
# exclusive
1...10
# first item in range
range.first
range.begin
# last item of range
range.last
range.end
# create array from range
array = [*range]
# works with string chars
('a'..'m').include?('g') # return true
- not obj but reference them
- all caps naming convention
- title case works but not advised
- not meant to change but changeable
- ruby will issue warning + change it
THIS_IS_CONSTANT = 40
this_variable = 55
This_is_also_const = 'but ill advised'
- aka null
nil.class # returns NilClass
# nil is not false
nil == false # returns false
# 3 methods to check if something is nil
nil.nil? # returns true
88 == nil # returns false
!nil # returns true, checks if it has a thing
!88 # returns false, because it exists
!variable
if boolean
# ...
elsif boolean
# ...
else
# ...
end
- like a reverse if
- can chain with else
unless boolean
# ...
end
# equivalent to
if !boolean
# ...
end
# example
unless array.empty?
# ...
end
# case with booleans
# very flexible
case
when boolean
# ...
when boolean
# ...
else # default (optional)
# ...
end
# case with comparisons
# simplified for comparing values
case test_value
when value
# ...
when value
# ...
else # default (optional)
# ...
end
boolean ? true_result : else_result
- great for setting default values
x = y || z
# same as
y ? x = y : x = z
# if x has a value, use it, otherwise, set x = y
# does not execute if x has a value
x ||= y
# same as
unless x
x = y
end
- only for single lines for readability
x = y unless x
puts "Hello" if greeting_enabled
# infinite by default, needs control methods
loop do
# ...
end
break # terminates whole loop
next # jump to next loop
redo # redo this loop
retry # restart whole loop
# example
i = 5
loop do
break if i <= 0
puts "Countdown: #{i}"
i -= 1
end
puts "Blast off!"
- do is optional here?
- break is built into this loop
# run while something is still true
while boolean
# ...
end
# run until something becomes true
until boolean
# ...
end
# example: do/break is built into this loop
i = 5
while i > 0 # or until i <= 0
puts "Countdown: #{i}"
i -= 1
end
puts "Blast off!"
- usually performs code on each item in a set ( array, hash, range ) of values to iterate through
# example: times
i = 5
i.times do
puts "Countdown: #{i}"
i -= 1
end
puts "Blast off!"
5.times { puts "Hello" }
1.upto(5) { puts "Hello" }
5.downto(1) { puts "Hello" }
(1..5).each { puts "Hello" }
- thing inside pipes == block variable
- only used inside its block
- next value become block variable as it iterates
5.downto(1) do |i|
puts "Countdown: #{i}"
end
puts "Blast off!"
num.times
1.upto(2)
5.downto(1)
num.step
range.each
(1..5).step
'str'.each_line
'str'.each_char
'str'.each_byte
array.each
['arr', 'ay'].each_index
[1, 2, 3].each_with_index
hash.each
hash.each_key
hash.each_value
hash.each_pair
# example
fruits = ['banana', 'apple', 'pear']
# most common usage
fruits.each do |fruit|
puts fruit.capitalize
end
# same as
for fruit in fruits
puts fruit.capitalize
end
- name files with
.rb
- shebang line at top:
#!/usr/bin/env ruby
- allows ruby scripts to be run anywhere
- tells unix to go to environment and ask it to tell which vers of ruby to use
# shell command: what shebang does
/usr/bin/env ruby -v
-
unlike
break
, which only stops looping but continues running the rest of the script -
exit
,exit!
-
abort ( msg )
- useful for including error message
- same as
puts msg
and thenexit
-
type
ctrl + c
- sends an interrupt signal and tells the program to stop running
puts # outputs a line return no matter what
print # all together on a single line
puts "Hello"
# same as
print "Hello\n"
gets
it from standard input, usually user input- code will stop and listen to the keyboard for the user to enter data and hit return
- gets is written on the same line as the printed/puts line
- every user input comes back as a string
- the line return is included in
gets
- usually
.chop
or.chomp
to remove line return (interchangeable) chop
removes last character of a stringchomp
removes last character of a string if it is a new line character (usually used)
- usually
# example
print "What is your name? "
respone = gets.chomp
puts "Hello, #{response}!"
# Hello, PicaQ!
Enumerables (included modules)
- Countable items
- Arrays
- Ranges
- Hashes
- Strings ( kind of, used to be, and not anymore )
- can act like enumerables but aren’t
- can enumerate by character or by byte
count
countableeach
allows iterating through set- requires having a code block that you can execute
each_with_index
first
,last
include?
max
,min
if comparable
- delimited by
do
andend
# execute block 5 times
5.times do
puts "Hello"
end
# same as
5.times { puts "Hello" }
- single-line blocks
- blocks return data without changing anything
- anything simple
- Multiline blocks
- Blocks that perform actions, make changes
- more complex executions
5.downto(1) do |i|
puts "Countdown: #{i}"
end
puts "Blast off!"
# same as
5.downto(1) { |i| puts "Countdown: #{i}" }
puts "Blast off!"
- can use commas to list additional block variables
- only need one block variable for an array
- hash allows assigning
key
andvalue
into separate block variables
scores = {low:2, high: 8, avg: 6}
scores.each do |k, v|
puts "#{k.capitalize}: #{v}"
end
$variable |
Global |
@@variable |
Class |
@variable |
Instance |
variable |
Local |
variable |
Block |
factor = 2
[1,2,3,4,5].each do |n|
puts n * factor
end
puts n # undefined local variable or method
n = 1
factor = 2
[1,2,3,4,5].each do |n|
puts n * factor
end
puts n # 1
find
map
inject
sort
merge
- methods that accept optional code blocks
- usually modifies the default behavior, like sort in a certain way
- can write custom methods that accept code blocks
.find == .detect
- only returns the first value it finds
.find_all == .select
- returns an array of all found values
.any? , .none?
- returns bools
.all? , .one?
all
is true only if all values match the boolean conditionone
is true IFF one value matches condition
.delete_if
- modifies array in-place
(1..10).find { |n| n % 3 == 0 } # 3
fruits = ['apple', 'banana', 'pear']
fruits.find { |fruit| fruit.length > 5 } # 'banana'
basket = {'apple' => 0, 'banana' => 1, 'pear' => 3}
basket.find_all { |k,v| v < 3 } # [["apple", 0], ["banana", 1]]
(1..10).any? { |n| n <= 5 } # true
(1..10).none? { |n| n <= 5 } # false
(1..10).all? { |n| n <= 5 } # false
(1..10).one? { |n| n == 5 } # true
nums = [*1..10]
nums.delete_if { |n| n <= 5 } # [6, 7, 8, 9, 10]
nums.delete_if { |n| n % 2 == 1 } # [6, 8, 10]
.map == .collect
- iterate through enumerable
- execute code block on each item
- add result of block to a new array
.map! or .collect!
for powerful/ destructive version by replacing contents of the existing array in-place
- num items in == num items out
- map from first element to second element
- always returns an array of results
# arrays
x = [*1..5]
y = x.map {|n| n + 1 }
x # [1, 2, 3, 4, 5]
y # [2, 3, 4, 5, 6]
z = x.collect {|n| n * 50 }
z # [50, 100, 150, 200, 250]
# hash will return an array
scores = { low: 2, high: 8, avg: 6 }
adj_scores = scores.map do |k, v|
"#{k.capitalize}: #{v * 100}"
end
# ["Low: 200", "High: 800", " Avg: 600" ]
fruits = ['apple', 'banana', 'pear']
# returns the result of the block of code
y = fruits.map do |fruit|
if fruit == 'pear'
fruit.capitalize
end
# does nothing here for apple thus evals to nil
end
# [nil, nil, "Pear"]
y = fruits.map do |fruit|
if fruit == 'pear'
fruit.capitalize
else
fruit # do nothing to fruit
end
end
# ["apple", "[banana]", "Pear"]
cap_fruits = fruits.map do |fruit|
puts fruit.capitalize
# but return value is nil
end
# Apple
# Banana
# Pear
cap_fruits # [nil, nil, nil]
inject == reduce
- “Accumulator” Ruby convention = memo
- remember the result aka memo
- block variable used to accumulated
inject(memo)
- if
memo
is not provided a value, it grabs the first element in the set and uses it as its value - return values matter because it’s the result of the block that gets used
(1..5).map { |n| n }
(1..5).inject { |memo, n| memo + n } # result of each operation stored in memo for the next operation
# 15
[3, 5, 7].inject { |memo, n| memo * n } # (3 * 5) * 7
# 105
[2, 4, 6].inject { |memo, n| memo ** n } # (2 ** 4) ** 6
# 16777216
(1..5).inject do |memo, n|
memo + n
x = 0 # the only value that gets remembered
end
# 0
(1..5).inject do |memo, n|
if n % 2 == 0 # when it’s not even it doesn’t add
memo + n
end
# the memo is nil when n % 2 != 0
end
# undefined method "+" for nil
fruits = ['apple', 'banana', 'pear']
# starting value is 0
size = fruits.inject(0) do |memo, fruit|
memo + fruit.length
end
# 15
longest = fruits.inject do |memo, fruit|
if fruit.length > memo.length
fruit
else
memo
end
end
# "banana"
mash = fruits.inject('') do |memo, fruit|
memo << fruit[0]
end
# "abp"
<=>
- Comparison operator
- “Spaceship operator”
- usually used with sorts
value1 <=> value2
-1 # if v1 < v2
0 # if v1 == v2
1 # if v1 > v2
1 <=> 2 # -1
2 <=> 1 # 1
2 <=> 2 # 0
value1 |
<=> |
value2 |
---|---|---|
-1 | < | move ← |
0 | == | stay |
1 | > | move → |
array = [5,8,2,6,1,3]
# smaller items move to the left
x = array.sort { |v1, v2| v1 <=> v2 }
# [1, 2, 3, 5, 6, 8]
# greater items move to the right
x = array.sort { |v1, v2| v2 <=> v1 }
# [8, 6, 5, 3, 2, 1]
# reverse the sort by reversing the items in the block
fruits = ['banana', 'apple', 'pear']
# sort has default sort order
x = fruits.sort
# ["apple", "banana", "pear"]
# provide block for custom sort order
# sorted by string length here
x = fruits.sort do |fruit1, fruit2|
fruit1.length <=> fruit2.length
end
# ["pear", "apple", "banana"]
# same as, but sort_by is a bit slower
x = fruits.sort_by { |fruit| fruit.length }
# custom sort order
# <=> is just returning one of 3 values
x = fruits.sort do |fruit1, fruit2|
case fruit1
when 'apple'; 1
when 'banana'; -1
when 'pear'; 0
end
# ["banana","pear","apple"]
- semicolons are rare and separate lines, but could just drop numbers into new lines
.sort!
,.sort_by!
- replaces contents
- sorts in-place
array = [5,8,2,6,1,3]
array.sort! { |v1,v2| v1 <=> v2 }
array # [1, 2, 3, 5, 6, 8]
- how do you sort an unordered set?
- it returns an array
- it converts the hash into an array and then it sorts
hash = { a: 4, c: 5, b: 3 }
hash.sort { |p1, p2| p1[0] <=> p2[0] }
# [[:a, 4], [:b, 3], [:c, 5]]
- hashes only
- Merges two hashes together
- blocks if you want to have rules
# keys not the same but two overlap
# values for :a are different
h1 = { :a => 2, :b => 4, :c => 6 }
h2 = { :a => 3, :b => 4, :d => 8 }
h1.merge(h2)
# { :a => 3, :b => 4, :c => 6, :d => 8 }
# for :a the second, newer key value wins in key conflict
# flipped: the new value merged in wins
h2.merge(h1)
# { :a => 2, :b => 4, :c => 6, :d => 8 }
- key conflict = when keys both exist in the hashes but have different or the same values
# this block only gets called when there’s a key conflict
# key = key in conflict
# old is in h1
# new is being merged in from h2
# when there’s a key conflict the new one should win
h1.merge(h2) { |key, old, new| new }
# when there’s a key conflict the old value wins
# { :a => 3, :b => 4, :c => 6, :d => 8 }
h1.merge(h2) { |key, old, new| old }
# { :a => 2, :b => 4, :c => 6, :d => 8 }
# keep the old one if it is smaller than the new one otherwise keep the new one
h1.merge(h2) {|k,o,n| o < n ? o : n }
# { :a => 2, :b => 4, :c => 6, :d => 8 }
# multiplies the old and new together when there’s a conflict
h1.merge(h2) {|k,o,n| o * n }
# { :a => 6, :b => 16, :c => 6, :d => 8 }
.merge!
- replaces hash contents by merging into the original hash
"hello"
.reverse.capitalize
1
.upto
(5) { ... }
[1,5,4,3,2]
.sort
{ ... }
- instructions to perform a specific task, packaged as a unit
- called functions in other languages
- but since ruby is OOP, call them methods
- define once and called many times
- DRY
- must be defined before it is called
- can be redefined
- lowercase with underscores
- first character can be letter or
_
- contain letters, digits, underscores
- last char can be
? ! =
?
convention to return bool!
convention to indicate more powerful/destructive version
- avoid using the same name for variables and methods
- defined with
def
keyword - can pass in different data in parens each time you use it
# everything from def to end is our method definition
def some_name
# ...
end
# must call the method to execute whatevs is inside the definition
def welcome
puts "Hello World!"
end
welcome
# Hello world!
$variable |
Global |
@@variable |
Class |
@variable |
Instance |
variable |
Local |
variable |
Block |
- local variables inside methods only have scope inside methods
- global, class, and instance have scope both inside/outside methods
value = 10
def output_value
puts value
end
output_value
# undefined local variable or method for 'value'
def set_value
value = 20
end
set_value
value
# 10
- method can recieve values at runtime (when called)
- number of args must match defined number in the same order
- local variables inside the method
def volume(x, y, z)
x * y * z
end
volume(2,3)
# ArgumentError:
# wrong number of Arguments
# (given 2, expected 3)
- optional
- methods with arguments use them
- methods without args don’t use them
- both for defining/calling
- convention: always add parens if there are args (but it will still work)
# works but highly discouraged
def volume x, y, z
x * y * z
end
volume 2,3,4
# 24
- any ruby object or expression can be used
[]
is common- required args first
- optional args at end
- least likely used args at the very end
def welcome(greet, name, punct='!')
greet + ' ' + name + punct
end
puts welcome('Hello', 'friend')
# Hello friend!
- maximum flexibility
- don’t have to provide any options
- if there are any, then code will look for it in the hash
def welcome(greeting, options={})
name = options[:name] || 'friend'
punct = options[:punct] || '!'
greeting + ' ' + name + punct
end
# skips over middle option
puts welcome('Hello', {:punct => '!!!'})
# Hello friend!!!
- methods return last operation’s value by default
- a return value of a puts line is nil
return
: returns a value and exits the method- useful in conditionals & loops
def subtract(n1, n2)
result = n1 - n2
result = 0 if result < 0
result # explicitly state last value
end
subtract(8, 3)
# 5
- best not to use
puts
orprint
in methods- methods that only return values are more flexible
- except for methods designed strictly for output
- i.e. have methods that do calculations and another that does output
- methods can only return one value (object)
- use an array or hash to return multiple Values
- then look inside to get the desired values
def add_and_subtract(n1, n2)
add = n1 + n2
sub = n1 - n2
[add, subtract] # returns array
end
# assign return value to result
result = add_and_subtract(8, 3)
# look inside to get values
a = result[0]
s = result[1]
# same thing with a hash
def add_and_subtract(n1, n2)
add = n1 + n2
sub = n1 - n2
{:add => add, :sub => sub}
end
# assign return value to result
result = add_and_subtract(8, 3)
# use keys to assign corresponding values
a = result[:add]
s = result[:sub]
# array return values only:
# multiple assignment using csv/ comma delimited list
# ruby automatically takes the elements and assign to those variables
a, s = add_and_subtract(8, 3)