We've used Strings, Integers, Floats, and Booleans.
What if you want a list or collection of data?
Ruby has two basic list types, Array and Hash.
Arrays store ordered, non-unique lists of any kind of data.
>> list = []
=> []
>> first_three = [1, 2, 3]
=> [1, 2, 3]
>> vector = [18.4, 12.8, 9.5]
=> [18.4, 12.8, 9.5]
Arrays can hold any types
# Strings
>> grocery_list = ["Juice", "Biltong", "Bunny chow", "Ostrich steaks"]
=> ["Juice", "Biltong", "Bunny chow", "Ostrich steaks"]
# Integers and Strings
>> zombies = [28, "days", "later"]
=> [28, "days", "later"]
# even nil, or other arrays
>> weird_array = [nil, [1, 2], nil]
=> [nil, [1, 2], nil]
Arrays use an index to determine the position.
The index starts at 0!
# index 0, index 1, index 2
>> ["Apple", "Banana", "Lemons"]
=> ["Apple", "Banana", "Lemons"]
The items in an array are called elements.
Access elements by using []
with the index.
>> fruits = ["Apple", "Banana", "Pear"]
=> ["Apple", "Banana", "Pear"]
>> fruits[0] # 0 is the index of the first element
=> "Apple"
>> fruits[1]
=> "Banana"
>> fruits[2]
=> "Pear"
car = "Toyota"
parking_lot = [car]
Remember, index 0 is the first element!
numbers = [1, 2, 3, 4]
letters = ['a', 's', 'd', 'f']
colors = ['green', 'red', 14, 12]
What is the value of...
numbers
?letters
?colors
?Q: What happens if you access an index that doesn't exist?
A: Ruby returns nil
.
>> fruits = ["Apple", "Banana", "Pear"]
>> fruits[3]
=> nil
>> fruits[100]
=> nil
Q: What about negative indeces?
>> fruits = ['Apple', 'Banana', 'Pear']
>> fruits[-1]
=> ?
>> fruits[-2]
=> ?
A: Negative indeces access from the end!
>> fruits = ['Apple', 'Banana', 'Pear']
>> fruits[-1]
=> "Pear"
>> fruits[-2]
=> "Banana"
# Out of bounds negative index is also nil.
>> fruits[-5]
=> nil
Hang with your group. Try making some arrays!
Here's a few for inspiration:
bananas = ['bananas', 'bananas', 'bananas', 'bananas']
weird = [nil, nil, nil, 1000000, nil]
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41]
# Create an array
>> todo_list = ["laundry", "pay rent"]
=> ["laundry", "pay rent"]
# Add an element to an array
>> todo_list.push("clean room")
=> ["laundry", "pay rent", "clean room"]
# Remove an element from an array
>> todo_list.delete("laundry")
=> ["pay rent", "clean room"]
<<
is called the append operator. It appends the argument to the object and returns the new object.
Ruby always prefers the <<
operator over the push
method.
# It works for Arrays
>> grocery_list = ['apples', 'oranges']
>> grocery_list << 'potatoes'
=> ["apples", "oranges", "potatoes"]
# And Strings too!
>> greeting = "My name is "
>> greeting << "Brian"
=> "My name is Brian"
>> greeting
=> "My name is Brian"
We can also combine and find the difference of difference of multiple arrays using the + and - and * operators.
>> first = [1, 2, 3]
>> second = [2, 3, 4]
# combining arrays
>> first + second
=> [1, 2, 3, 2, 3, 4]
# difference of arrays
>> first - second
=> [1]
# multiplying arrays
>> broken_doorbell = ['ding', 'dong'] * 4
=> ["ding", "dong", "ding", "dong", "ding", "dong", "ding", "dong"]
Sometimes we only want unique elements from an array. We can use uniq
for this:
>> [1,2,3,4,4,5,5,6].uniq
=> [1,2,3,4,5,6]
>> ([1,2,3,4,5] + [2,3,4,5,6]).uniq
=> [1,2,3,4,5,6]
>> "bring me juice".split
=> ["bring", "me", "juice"] # the .split method returns an array!
>> "first,second,third".split(",")
=> ["first", "second", "third"]
>> "www.google.com".split(".")
=> ["www", "google", "com"]
Here are some example arrays to get you started.
fruits = ['Apples', 'Oranges', 'Bananas']
numbers = [1, 3, 4, 2, 7, 4, 9, 10, 10, 10]
no_data = []
Hang out with your group and play around with these common ruby Array methods:
.class | .empty? | .first | .include? |
.last | .length | .reverse | .sample |
.sort | .uniq |
We could do this:
puts fruits[0]
puts fruits[1]
puts fruits[2]
puts fruits[3]
puts fruits[4]
puts fruits[5]
puts fruits[6]
puts fruits[7]
Shame! Repeating yourself isn't lekker, boet!
To display all of the items, we use an iterative loop to enumerate through all of the elements of the array.
fruits.each do |fruit|
puts fruit
end
Let's see what this means!
Loop: A set of code that will repeat indefinitely
# Don't run this in pry
loop do
puts "I'm looping forever!"
end
Iteration: Running code in a loop until a particular condition is met.
i = 0
while i < 10
puts i
i = i + 1
end
Enumeration: An iteration where the condition is simply doing something to each item of a collection.
>> fruits = ["Apple", "Banana", "Pear"]
>> fruits.each do |fruit|
>> puts fruit
>> end
Apple
Banana
Pear
=> ["Apple", "Banana", "Pear"]
array.each do |item|
puts item
end
each
takes a block and runs that code on each element.Loop: A set of code that will repeat indefinitely
Iteration: Running code in a loop until a particular condition is met.
Enumeration: An iteration where the condition is simply doing something to each item of a collection.
Enumeration iterates through a collection one time using a code loop.
Block: A block of that is passed into methods that can take a block as an argument.
.each runs a block of code on each element in the collection, and returns the original array
numbers = [1, 2, 3]
numbers.each do |element|
puts element
end
1
2
3
=> [1, 2, 3]
.each_with_index is just like .each except that it also gives you the index of the element along with the element.
# Same as above, but simpler
fruits.each_with_index do |fruit, index|
puts "Fruit #{index}: #{fruit}"
end
Fruit 0: Apple
Fruit 1: Banana
Fruit 2: Pear
=> ["Apple", "Banana", "Pear"]
.map runs a block of code on each item in the list, and stores the return values of each block. This transforms the original array into a new one.
transformed_array = [1, 2, 3].map do |element|
element * 3
end
=> [3, 6, 9]
.push
, <<
, and .delete
methods.each
, .each_with_index
, .map
+
, -
, and *
Hashes are also often called Dictionaries, because they work the same way.
dictionary = {
"win" => "be successful or victorious in (a contest or conflict).",
"lose" => "be deprived of or cease to have or retain (something)."
}
>> numbers = {'Erica' => 123456789, 'Aaron' => 567890934}
=> {'Erica' => 123456789, 'Aaron' => 567890934}
>> numbers['Erica']
=> 123456789
>> numbers['Aaron']
=> 567890934
>> numbers['Nobody']
=> nil
A hash maps a set of unique keys to values.
You can never have two values for the same key.
The key/value pair is also referred to as an element.
>> numbers = {'Erica' => 123456789, 'Aaron' => 567890934}
=> {'Erica' => 123456789, 'Aaron' => 567890934}
# this returns an Array
>> numbers.keys
=> ['Erica', 'Aaron']
# this also returns an Array
>> numbers.values
=> [123456789, 567890934]
>> numbers.has_key?('Erica')
=> true
>> numbers.has_key?('Nobody')
=> false
>> numbers = {"Erica" => 123456789, "Aaron" => 567890934}
=> {"Erica" => 123456789, "Aaron" => 567890934}
# Adding a new element
>> numbers["Ian"] = 892381232
# Removing an element (aka key/value pair)
>> numbers.delete("Aaron")
# Final state with Aaron removed and Ian added
>> numbers
=> {"Erica" => 123456789, "Ian" => 892381232}
The order of the elements in a Hash is not reliable.
Treat them like random grab bags.
# Enumerating with an array
numbers = [1,2,3]
numbers.each do |number|
puts number
end
# 1
# 2
# 3
# Enumerating with a hash
phone_numbers = {'Erica' => 123456789, 'Aaron' => 567890934}
phone_numbers.each do |key, value|
puts value
end
# 123456789
# 567890934
Create some hashes, iterate over them, and add/remove elements from them.
Here's an example:
hash = { 'one' => 1, 'two' => [1, 2], 'three' => 'three' }
Hash ideas: - family members and their ages - classmates and their phone numbers - recipes (bonus, ingredients are an array)
Symbols are a more computer-friendly way of using strings in code.
"name" # inefficient
:name # efficient
Symbols are written with a colon followed by a snake_case string. No capitals or numbers in symbols!
This is good style:
:name
:next_page
This is legal but not good style.
:HelloThere # capitalizing words
:summerof69 # using numbers
Two ways of writing hashes when using symbols
>> health_info = {:age => 30, :weight => 150}
=> {:age=>30, :weight=>150}
>> health_info = { age: 30, weight: 150 }
=> {:age=>30, :weight=>150}
The second way is strongly preferred in modern Ruby, because it's much easier to type.