Another blast from the past: My simple contact list program!
The assignment itself is from Treehouse, but I made a few of my own changes.
My first time doing this assignment was over a year ago. At the time, I was scratching my head at certain parts (like creating methods and how return works, exactly).
But not today! I delve into every nook and cranny! Nothing goes unexplained here.
Let’s start by looking at a sample run.
This program:
Continuously prompts the user for contact names
For each contact, continuously prompts the user for phone numbers
We need to write a program that does the following:
Asks the user to enter the following info about their closest friends:
name (not necessarily unique)
age
favorite color
Outputs total number of friends under 18, followed by their names
Outputs total number of unique colors, then lists them
Should use at least:
1 array
1 hash
My solution includes:
a .times loop based on user input
an array of hashes
two other arrays for printing purposes
Initializing
First, I initialized a) my main array, and b) a method for collecting user input.
The array is created with a simple [] — and then assigned to the variable name friend_data.
My method is called ask, and the argument it accepts is called question. It prints question, and collects answer with gets.chomp.
(Note: I used to think I’d need to add return answer…but Ruby automatically does this for us. Neat!)
Collecting input
Next, I collected and stored input from the user.
After printing a message to the user, I call my ask method. The argument is a question asking about the number of friends.
User input is then assigned to variable number_of_friends.
Here’s what that looks like:
After that, I start a .times loop. The value inside number_of_friends determines how many times the loop runs. (Index is represented by i.)
I call my ask method three times — to ask about and collect values for name, age and favorite_color. (.to_i converts age to an integer, while .downcase converts all answers for favorite_color into lowercase letters.)
Lastly, the .times loop creates a new hash inside the friend_data array. Each hash represents a single friend (and is assigned to index i inside friend_data).
Key :name is connected to the value inside name, key :age is connected to the value inside age, and key :favorite_color is connected to the value inside favorite_color.
Why did I use an array of hashes?
Hashes are good for storing pairs of information. An array of hashes means I can use the same keys for each friend — which makes it easier to juggle friends with, say…the same name.
The loop ends when it finishes collecting data for each friend.
Here’s what that looks like:
Friends under 18
Next…
Calculating and printing number of friends under 18
Printing their names
First, I printed a message that interpolates the number of friends who’re under 18.
Ruby finds this number by:
Calling the .count method on friend_data
Passing a block that:
Iterates over each element in the array (friend)
Only counts friend if friend[:age] is under 18
Okay, so now we know how many friends are under 18. But what about their names?
I wanted to give the printed names an array-like format. For simplicity’s sake, I decided to use arrays!
First, I initialized a minor_friends array with []. Then, I iterated over each hash in friend_data with the .each method.
Passing a block, I told Ruby to send friend[:name] into minor_friends — but only if that friend’s age is under 18.
Then, I printed the contents of minor_friends onto a single line (with print).
How? By:
Converting the array to a string (with .to_s)
Using gsub to erase each instance of " in the list
Here’s the result:
Number of unique colors
Finally, I needed to:
Calculate and print the number of unique favorite colors
Print a list of each unique color
Like the original assignment prompt suggested, I turned to the .uniq method. (Read more about here.)
Wrestling with uniq
The .uniq method returns a new array by removing duplicate values in self.
So, for example, [1, 1, 2, 2, 3, 3, 4, 4].uniq would return a new array of [1, 2, 3, 4].
However, friend_data.uniq won’t behave quite the same way. Why? Because each element is a hash with multiple key-value pairs.
What do I mean? Well, friend_data.uniq will return an array of hashes…for each friend with a unique color:
Combining this syntax with .count tells us how many unique colors we’ve got:
Cool! (This is how I interpolated the number of unique colors in my array.)
Based on the above, I expected to be able to print out an array of just the colors. HOWEVER…
You’ll notice blue appears twice. These aren’t unique colors.
Moreover, this doesn’t even return an array! AHHHHH.
Pretty frustrating, to say the least!
This is what I ended up doing (for now):
Call the .each method on friend_data
Pass a block that shovels friend[:favorite_color] into unique_colors
…IF friend[:favorite_color] wasn’t already included in unique_colors
How did I figure out if the current color was already included in unique_colors? With an include? statement!
After that, I used .to_s and gsub to print the array in a pretty list.
Here’s what the output looked like:
Summary
I made a program to collect names, ages and favorite colors of the user’s friends.
I asked the user for a number of friends, and creating a .times loop that runs based on that number.
Then, I collected info for each friend — and stored that information inside an array of hashes. Each hash represented one friend.
(Arrays of hashes make it easier to store pairs of info, without requiring each friend to have unique keys.)
After that, I created two more arrays to neatly print a) friends who are under 18, and b) unique colors inside of the initial array.
This exercise was a fun way to practice iterating over an array of hashes, and accessing certain values from those hashes.
It was also a cool introduction to the uniq method (which returns a new array without any duplicates).
Symbols are Ruby objects with specific names. In a program’s execution, you can only have one symbol with the same name.
For example, let’s say we assign the symbol :age to variables x and y. Variables x and y will have the same object ID number.
Meanwhile, we could also assign the string "age" to variables a and z.
Even though a and z hold the same sequence of characters (and they’ll evaluate as the same if we compare them), they’re actually different objects. We know this because they have different object ID numbers.
In a smaller program, it’s not such a big of a deal.
But symbols are a great way to save memory, since they’re the same object — no matter how many times you call them! That’s why, when making hash keys, we typically use symbols instead of strings.
Good rule of thumb:
If the contents of individual characters are important: Use a string!
If you’re just naming something: Use a symbol!
Convert string to symbol
We can actually convert strings to symbols. Here’s how:
With the .to_sym method! For example, "apple".to_sym creates an :apple symbol.
Symbols and hashes
Normally, you need a preceding colon when making a symbol by itself.
However, with hashes, there’s a handy shortcut!
In this case, the colon connects each key and value.
(Note: Combining this syntax with preceding colons will give you an error.)
HOWEVER, when accessing keys? You DO need the preceding colon.
In short…
DON’T put the preceding colon when defining a symbol inside a hash
DO put the colon when accessing it
Arrays vs. Hashes
This section talks about arrays and hashes — how they’re different, how they’re the same, and when to use what!
Arrays
Arrays are a list of ordered items.
(I personally like to imagine them as a grocery list of items.)
They have integer indices starting from 0…which cannot be changed. In other words, if we want to access an element inside an array, we must use an integer.
Hashes
Hashes are a collection of paired information — often called key-value pairs.
(I personally like to imagine them as dictionaries!)
In this case, an object (the key) becomes the index. Keys in a hash are unique (they cannot be repeated). Values are not unique (so they can be repeated).
Unlike arrays, hashes are NOT ordered.
Similarities
Both are collections that store and retrieve data.
In either case, we will get nil back when trying to access an element that doesn’t exist.
Differences
Arrays have a fixed index (based on integers, starting at 0).
Hashes let you DEFINE the index. That index is your key that’s connected to a value.
Tips
Storing single pieces of info? Use an array!
Data better described as a pair? Use a hash!
Examples
Here are some examples of how to use: arrays, hashes, and…arrays and hashes!
Example 1: Storing names inside an array
We’re only storing people’s names here.
In other words, we’re collecting single pieces of information. So, we keep them inside an array!
Example 2: Storing names and ages inside a hash
This example stores students’ names and ages.
Each person has both a name and an age. In other words, we’re working with pairs of information.
When working with pairs of info, we store them inside a hash.
In this case? Names (assuming they’re all unique) are the keys. Ages are the values.
What if the names AREN’T unique? In that case, an array of hashes could work. (See last example.)
Example 3.1: Storing names, ages and favorite colors (inside a hash)
In this case, each student’s name functions as a key.
But! Each key’s value is a hash. The hash stores each student’s age and favorite color.
If the names aren’t unique, we could potentially store this information inside an array. Like so…
Example 3.2: Storing names, ages and favorite colors (inside an array of hashes)
Hashes
Like we talked about above, hashes are a collection of paired information (or key-value pairs).
Any object can be the key. However, keys inside a hash must be unique.
Information is stored in the order it was entered — however, you shouldn’t rely on this order when working with hashes.
Creating a hash
There are a few ways we can create a hash.
Example 1: Creating an empty hash
What we need: Some sweet curly braces. That’s it!
Example 2: Creating a hash with values
We can also create an array already populated with values.
Here’s how: We simply combine a symbol and a colon ( : ), followed by the value.
If we want multiple values, we separate them with a comma and a space.
Example 3: Creating a hash without symbols
Keys don’t have to be symbols. They can be any other object, actually.
But! We can’t use the notation in Example 3 if our key isn’t a symbol. Instead, we use the hash rocket notation ( => ).
We could make the values symbols. In that case, we still need to use the preceding colon ( : ).
(Note: If your keys are Fixnums, it’s probably better to use an array.)
Storing and retrieving data from a hash
Here’s one way to store data into an already-existing hash:
hash[key] = value
Once we create a hash, we can access the key-value pairs stored inside.
Here’s an example:
In this case, students[:Riley] returns the value of 29.
Iterating over a hash
Iterating over a hash is similar to an array. The main difference is, we can work with keys and their values (instead of elements and their indices).
Here’s an example:
These loops go over each item in the hash, and return both the key and value. (In this case, name represents the key, and age represents the value.)
We can also iterate over keys only. Like so:
These loops go over each item in the hash, and return just the key. (In this case, name represents the key.)
Similarly, we can also iterate over values only. Like so:
These loops go over each item in the hash, and return just the key. (In this case, age represents the key.)
Useful hash methods
Here are some commonly-used hash methods…
h.clear — Remove all key-value pairs from hash h
h.empty? — Return true if hash h contains no key-value pairs
h.length — Return the number of key-value pairs in hash h
h.keys — Return a new array with the keys from hash h
h.values — Return a new array with the values from hash h
h.key(value) — Return the key of an occurrence of a given value in hash h
h.key?(key) — Return true if key is present in hash h
h.value?(value) — Return true if value is present in hash h
Examples
The original instructions say to do these a) in pairs and b) on paper.
I did them on paper, at least. But since my phone takes blurry pictures, I went ahead and did them on my computer too. So here we go!
Example 1
We want to be able to quickly determine the state abbreviation for the states in the United States.
Determine whether it is best to use an array or a hash, and create it storing at least 4 states and their abbreviations.
We’re storing two pieces of information: state names and abbreviations. Hashes are the best way to go!
Then write code to print out only the states, then only the abbreviations, and finally nicely formatted output displaying both the states and their abbreviations (e.g., The abbreviation for Nebraska is NE)
Example 2
We want to be able to quickly determine the amount of different types of food items you have in your house.
Determine whether it is best to use an array or a hash, and create it storing at least 4 food items and their quantities.
We’re storing two pieces of information: types of food, and their respective amounts. So, we definitely want to use a hash…
Then write code to print out all the items and their quantities using nicely formatted output.
Summary
This lesson covers symbols, how arrays compare to hashes, and hashes themselves.
In Ruby, symbols are objects with specific names. In a program’s execution, you can only have one symbol with the same name.
They’re a great way to conserve memory in larger programs (unlike using different strings).
If the content of individual characters are important, we use strings. If we’re just naming something, though, symbols are the way to go!
Arrays are an ordered list — kind of like a list of grocery items. Elements follow a 0-based index made up of integers. This index cannot be changed.
Hashes store pairs of information — kind of like a dictionary. Values are connected to keys (instead of an index).
Use an array if you’re storing a single piece of information. If info is better described in pairs, use a hash.
Hashes are created with curly braces — with or without symbols. The syntax we use depends on whether our keys are symbols.
Similar to arrays, we can iterate over the contents of a hash. However, we have a few more options, namely:
Iterate over keys only
Iterate over values only
Iterate over both keys and values
Some cool things I learned
In no particular order:
We can convert strings into symbols with .to_sym (handy for user input)
Keys can be considered the “index” inside of a hash
Making a hash of hashes is one way to bypass the “keys must be unique” restriction
We can create arrays of keys or values (with .keys and .values, respectively)
And since my last post got pretty long, I decided to do a separate entry for optional stuff.
So, here we go!
Exercise 1
Create an array of people that are invited to a party. Allow the user to ask if a certain person is invited to the party. If the person is on the invitation list respond INVITED otherwise respond NOT INVITED.
Here’s my code:
What my code does:
Create an array of people invited to the party
Prompts user to enter a name
Collects user input
Based on user input, prints INVITED or NOT INVITED
Checking whether a name is on the list is fairly straightforward.
First, I assign user input to variable name.
Then, using an if-else statement, I check whether name is included in the invitees array. An .include? method (with name entered as the argument) does the job quite nicely!
If invitees.include?(name) evaluates as true, then the program prints INVITED. Otherwise, it prints NOT INVITED.
Sample run:
Since “Claire” is one of the elements inside the invitees array, the program prints INVITED.
Another sample run:
“Kathleen” is NOT an element inside the invitees array, so the program prints NOT INVITED.
Exercise 2
Have the user enter in a sentence. Then, using an array, store the frequency of each letter of the alphabet from the sentence. Print out the frequency of each letter. Do not count uppercase and lowercase letters differently.
Here’s my code:
What my code does:
Collect user input
Checks the frequency of each letter of the alphabet
Stores each letter’s frequency inside an array
Prints the frequency of each letter that actually appears inside the sentence.
After prompting the user to enter a sentence, I assign the string to variable sentence.
Since we’re not supposed to count uppercase and lowercase letters differently, I convert all letters to lowercase with .downcase.
Then, I pass "a".."z" as a range for an .each loop. Each letter is represented by the letter variable.
Here’s how I actually calculate the frequency of letters: By entering letter as an argument for the .count method…which I called on sentence.
The resulting value goes into my frequency_of_letters array.
After that, I use the .each_with_index method with a range of "a".."z". Each letter is represented by letter, and i represents the current letter’s index number.
Then, in curly braces, I tell the program to print a) the current letter, and b) its corresponding frequency inside frequency_of_numbers .
However, if the current letter appears 0 times in the sentence, then it won’t be printed (courtesy of a quick unless statement).
Sample run:
Another sample run:
Exercise 3
Create an array of size 8. Fill the array randomly with 0’s and 1’s. Print out the array so that it appears as a binary number. Calculate and print out the decimal value of that binary number.
Here’s my code:
First, I create a new array for 8 elements with Array.new(8). The following block uses rand() — and tells Ruby to generate a random number between 0 and 1 for each position.
The resulting array is assigned to variable binary.
Here’s how I printed the list as a single binary number: Call the .join method on binary…and print the result on a single line!
Now, to calculate the random binary number’s decimal value:
Initialize a new variable decimal with the value 0 (for the upcoming loop)
Tell an .each_with_index method to cycle through each element in binary
Use .reverse to iterate through each element — from last to first position
Assign current number in binary to num, and its index number to i
Tell Ruby to calculate 2 to the power of the number’s current index…
…And add the result to the current value inside decimal!
ONLY perform the above calculations if num equals 1
Finally, I print the result by using puts on decimal.
Sample run:
Summary
These optional exercises gave more opportunities to store, print and calculate information with arrays!
Today, I did the required problems for JSL Day 5. (Stay tuned for the optional problems!)
Exercise 1
Create an array to store 5 names. Have the user enter in the 5 names and then print out the 5 names in all UPPERCASE all on the same line. Note that the user may not enter all the names in uppercase.
Here’s my code:
Here’s what my code does:
Prompts the user to enter five names
In a single loop: Collects, then stores names inside of a new array called people
Prints the names on the same line, in uppercase
Collecting input and creating an array
Okay, so…I wanted to try something a little different.
First, I prompted the user for input. (Nothing new there.)
Here’s what I’d normally do after that: Assign the input to a variable…and then shovel it on over to the array.
This time, however, I inserted a handy-dandy gets.chomp inside the loop that’s populating my array — and it worked!!
For formatting, curly braces probably aren’t the best. Because if you’re printing stuff and collecting input with each iteration, a regular do...end block would be better (since it’s good for multiple lines of code).
But…man! It’s just so cool to do all that in a single line of code. (I’m officially in love with passing blocks with curly braces. ❤ ❤ ❤ )
AHEM…anyway.
My loop ran 5 times (once for each element in the new array). The resulting list was assigned to variable people.
Printing ALL the names
These names need to be 1) on a single line, and 2) all UPPERCASE.
I used a .times loop. With i as the index, it iterates through each item in the people array (inside some curly braces). Each element is printed, and changed to uppercase with the .upcase method.
The loop is set to run a number of times equal to people.length (or, 5) minus 1. In other words, this .times loop runs 4 times.
But, wait…aren’t there 5 elements in the array we’re printing? So, why is this loop only running 4 times?
I’ll tell you why: We’re fence-posting it!
In other words, I’m printing one name (in this case, the last element) outside of the block. I’m doing this so the names can be spaced properly (and there won’t be a rogue space hanging out after the final name is printed).
Of course, I need everything on the same line. Thankfully, prints has me covered.
(Note to self: Looking back, the section on Fencepost Problems only gives .each loops as an example. Will need to investigate that later…)
Here’s a sample run:
++++++
Exercise 2
Create an array to store 3 boolean values entered in by the user. Print out YES if all the values are true and NO if at least one value is false.
Here’s my code:
Here’s a breakdown of what my code does:
Collect three strings (either "true" or "false") from user
Keep prompting user for input if string doesn’t equal “true” or “false” (my idea)
Convert strings into boolean values
Store boolean values in an array
Print YES if all values are true; print NO if at least one value is false
Collecting user input
Before doing anything, I created an empty array using []. This array is called boolean_values.
Next, I initialized a variable called true_or_false. It stores user input during the next step.
Right below that, there’s a .times loop — set to run 3 times, since that’s how many boolean values we need from the user.
What happens if the user doesn’t want to cooperate?
Well, I’m glad you asked. That’s why I nested an until loop in there.
This until loop prompts the user to enter either “true” or “false”…and assigns the input to variable true_or_false.
Until true_or_false equals "true" OR "false", the program keeps asking for input. In other words, the user MUST enter "true" or "false" if they want to continue.
For consistency (and ease of evaluation later), I went ahead and converted the input to lowercase using the .downcase method.
Converting input to boolean values
After that, an if-elsif statement evaluates true_or_false. If it contains the string "true", then it converts to true — a true-blue boolean value!
Likewise, if true_or_false equals "false", then it’s converted to the boolean value false.
After that, the current value of true_or_false is shoveled into the boolean_values array.
…And the cycle continues two more times!
Because true_or_false no longer contains a string, the nested until loop works the same as before.
Printing YES or NO
Pretty straightforward! An if-else statement evaluates each element in the boolean_values array
Here’s how: In the first line, a .include? statement checks boolean_values includes false. If so, then it prints NO — even if only one value is false.
Otherwise, it just prints YES. (All values must equal true for the program to execute this line.)
Here are some sample runs:
++++++
Exercise 3
Create an array to store the following words: firetruck, fire drill, fire hydrant, firefighter, fireproof, fire station, fire hose. Then write code that uses the array to print out truck, drill, hydrant, fighter, proof, station, hose without modifying the array.
Here’s my code:
Curly braces make another appearance in this one!
But first, I create the array of words — named fire_words, in this case.
After that, I write a single-line .each loop using curly braces. Iterating over each word in fire_words, it prints each element without the word “fire”.
How? First, .gsub substitutes all instances of "fire" with "". For formatting purposes, the .split method removes any leading whitespaces (that words like “fire drill” or “fire hydrant” end up with).
For my own reference, I printed out the array — and verified that the contents of fire_words are unchanged.
Here’s the output:
++++++
Exercise 4
Create an array to store the amount of money spent on 4 people during the Holidays. Have the user enter in the amount spent on each person. Print the total spent on all the people. Total money spent should be displayed with a dollar sign, decimal point, and only 2 digits after the decimal.
Here’s my code:
Collecting input and creating an array
First, I create a new array with 4 elements.
Passing a single block as a parameter, I fill each position with input from the user. (The method .to_f converts these values to a float)
The result is a list assigned to holiday_spending.
Calculating the total
The .reduce method reduces the values inside an array to a single number. (It can add them together, in other words.)
Our starting value of 0 is entered as an argument. Then, a single block adds each element inside holiday_spending to 0. The total is represented as result, which is then assigned to variable total.
Printing the total
Last, we print a message to the user. While we could simply refer to total, that alone won’t give us two decimals.
Instead, we must put '%.2f' % total to convert total to a float with only two digits.
A sample run:
++++++
Exercise 5
Create an array to store 5 random numbers (between 1 and 10, including both 1 and 10) generated by the computer. Print out the square of each of the elements in the array.
Here’s my code:
Creating an array
First, I use Array.new. I pass 5 (the number of elements) and a block (that generates random numbers for all 5 positions) as parameters.
How do I generate the random numbers themselves? With rand(1..10), of course! In this case, I’m passing (1..10) as the range. (Two dots means 10 is included.)
The resulting list is assigned to variable numbers.
Printing the square of each element
Calling the .each method on numbers, I iterate over each number (represented by num).
For each element in numbers, the loop prints a string — that includes num, as well as the square of num. (The square of num is calculated by interpolating num * num.)
Sample run:
++++++
Exercise 6
Create an array to store 5000 decimal numbers. Randomly generate numbers from between 0 – 1 (including 0, but not including 1) to fill the array. Calculate and print the mean of all the elements in the array.
Here’s my code:
Creating an array of 5,000 numbers
Calling Array.new, I pass 5,000 and a single-block as parameters. (Note: I type 5_000 for readability.)
Our single-block parameter consists of rand(0...1.00). Two thing to note here:
In order to generate decimals, 1 must be a float (or 1.00)
Three dots excludes 1.00
The result is a new array with 5,000 randomly-generated decimal numbers between 0 and 1.
Calculating and printing the mean
Next, I print the mean. But first…I’ve got to calculate the dang thing!
Here’s how…
Step one: Reduce the values inside numbers to a single value with .reduce(0).
This also requires a block, with result starting out as 0. The loop increments result by the current value (or current, in this case).
I’ve wrapped this process in parentheses…so Ruby will know to calculate this before doing the next step.
Step two: Divide the resulting value from step one, by the number of elements inside numbers.
In this case, we need to divide by 5,000.
Although I certainly could have hard-coded 5,000, I used something more flexible: numbers.length (or, the number of elements inside numbers…which happens to be 5,000).
Inserting puts before these calculations prints our mean to the screen!
A sample run:
Summary
Today’s exercises relied on arrays — and included several opportunities to use material covered in previous lessons
Some things I practiced:
Generating random numbers using rand() — with exclusive and inclusive ranges
Populating a new array…by passing a block as a parameter for Array.new
Altering an array’s elements without changing the array itself
Performing calculations on elements inside an array
Sometimes we create a variable without storing anything in them. When a variable doesn’t have anything stored in it, it automatically has a value of nil.
The .nil? method lets us check if something is nil.
Let’s look at some examples!
Things aren’t automatically nil in the beginning.
In this example, puts x triggers an error (instead of printing nil).
Why? Because x hasn’t been declared.
For x to hold nil, we must declare it…without giving x the value we want.
Here’s an object that returns nil:
This code prints There is no value in index 1
nums[1] points to index position 1. However, the single element in nums actually occupies position 0.
In other words, there is currently no value stored inside nums[1]. That’s why it returns nil.
But what happens if there is a value inside nums[1]?
This time, nums has two elements — 1 (in position 0) and 2 (in position 1).
The thing inside nums[1] is 2. …And so nums[1].nil? returns false.
As a result, our program executes the statement under else — and prints 2 is in index 1.
Arrays
Arrays are a container of things. They are organized in order from first to last…with a 0-based index. (Documentation here!)
Why Arrays?
Well, the alternative is to create individual variables.
That’s all well and good for smaller bits of data — but a huge pain if you’re, say…recording daily temperatures for the month of December.
Managing 31 separate variables just for temperatures would be annoying, sure.
But scale it to 365, 730, or 1,095. Now you’ve got a real nightmare on your hands!
That’s where arrays come in handy. We can easily assign all of December’s temperatures to a single name. Best of all, we can access a specific day’s temperature if we need it.
Array Indices
An array’s index starts at 0, and increases by 1.
If you’re new to programming, that might sound weird. (It did to me!)
Zed Shaw compares arrays to a deck of cards — because you can pick any element at random. Consequently, having the indices start at 0 makes the math much easier in the long run.
The array nums has three elements. The first is 34 (in position 0), the second is 78 (in position 1), and the third is 45 (in position 2).
However, nums[3] returns nil in Ruby. Why? Because there’s no fourth element (and nothing in position 3).
Negative index numbers are also a thing!
The very last element in nums is at nums[-1].
The second-to-last element in nums is at nums[-2].
Third from the end is nums[-3].
However, nums[-4] returns a value of nil. That’s because we only have three elements stored in nums.
Create
You can create new arrays several different ways. Here are some of the basic methods:
Using []
Array.new
Using [] is the most basic way to create a new array.
If we leave [] empty, it creates an empty array. We can also store elements inside [] when we create the array.
Here’s another way to create an array:
Using Array.new by itself will create a new, empty array.
Something cool: You can fill your array with nifty things if you pass stuff into the parentheses!
For example, Array.new(3) creates an array with a length of 5. (It stores nil in each index.)
What if we want to start our array with something other than nil? Just pass a second value!
Here’s how: Array.new(3, "Ada") creates an array with a length of 5. It stores "Ada" in each index.
The final bit of code is an example of passing a block.
Array.new(5) makes a new array with a length of 5, which is assigned to mult2. As a second parameter, we pass { |i| i * 2 }.
What’s going on? Well, i gets the indexes of the array (in this case, 0 through 4).
Then, it takes that i and multiplies it by 2. So we get…
0 * 2 (returns 0)
1 * 2 (returns 2)
2 * 2 (returns 4)
3 * 2 (returns 6)
4 * 2 (returns 8)
The result? A new array populated with five even numbers!
Printing an Array
If we puts an array, it’ll display each element on its own line. (For smaller sets of data, that’s fine…but for larger sets, it’s a hassle.)
If we print an array, we’ll get a neat list with square brackets and commas. (Pretty!)
Lastly, we can also combine puts with string interpolation. This gives us the same square-brackets-and-commas layout. (Pretty!)
Change and Access
We can change and access an array with its indices.
For example, it’s possible to assign new values to a specific index number.
In this case, list[0] originally holds a value of 0. However, 7 is assigned to list[0] — and replaces 0 as the value.
We can also replace list[1] and list[2] in the same way:
Afterward, we access these new values via their respective index numbers.
Add to End
There are two ways to add elements to the end of an array:
.push
shovel operator ( << )
Here are some examples:
If we pass "banana" into .push, we can add that string onto the end of list.
Something cool about .push: You can pass multiple values at once. They’ll get pushed onto the end of the array in the same order. (RAD!)
Now let’s take a look at shovel…
The << operator can shovel a value to the end of an array.
BUT! You’ll get an error if you try passing more than one value (unlike .push).
Be careful not to accidentally stick a new array onto the end of your existing array!
Iterating over an Array
Iterating is another way of saying “looking at each one of the elements and performing some sort of action with them”.
Two different ways to iterate over an array:
do ... end block (for multi-line blocks)
curly braces {} (for single-line blocks)
Let’s look at a couple of examples:
The do ... end block and curly braces do the exact same thing here.
Style-wise, the curly braces are better. The reason is because we’re using a single-line block.
They both print out 1, 2 and 5 (or each element currently inside nums).
Another example:
While .each gives us each element in a collection, .each_with_index actually gives us both the element and its index number. Nifty!
This example prints out:
0: 1
1: 4
2: 6
Style-wise, the curly braces are the better choice. That’s because we’ve only got one line of code in our block.
Useful Array Methods
Here’s a list of handy-dandy methods for arrays:
arr.length — Returns number of elements inside arr
arr.empty? — Returns true if there aren’t any elements in arr (false, otherwise)
arr.first — Returns the first element in arr
arr.last — Returns the last element in arr
arr.take(n) — Returns the first n elements in arr
arr.include?(element) — Returns true if element is stored in arr (false, otherwise)
There are plenty of other methods too…but I probably use these the most!
Exercises
Apparently, JSL participants go over these in person. But I figure I may as well do these too.
Exercise 1
Create an array which will store the square of each value between 2 and 5, inclusive.
Both examples do the same thing. They create an array, and store the square of each value between 2 and 5, inclusive.
In the first case, we use a do...end block. The range is set with (2..5), and .each is used on the collection. We use the .push method to add the square of each value being passed through i.
In the second case, we use curly braces to pass a block through Array.new. The 4 tells Ruby we want 4 elements inside of list.
Using i as the index number, the curly braces calculate the square of each number between 2 and 5.
It does this by adding 2 to i (which starts at 0)…twice. Then, the block multiplies both sums together — and pushes the new value into the corresponding position of i.
For example, the first index is 0. And (0 + 2) * (0 + 2) equals 4. Therefore, my first element is the square of 2: that is, 4.
Exercise 2
Given an array that contains three people, Ada Lovelace, Annie Easley, and Margaret Hamilton (1) Add one new person of your choice, (2) Output Annie Easley using the array, (3) Replace Ada Lovelace with Megan Smith
First, the array people is defined with a list of three strings.
Next, I pushed “Frances Spence” into the array with shovel.
After that, we output Annie Easley by referencing its index number (1, in this case).
Lastly, we assign "Megan Smith" to people[0]…which was previously occupied by “Ada Lovelace". In other words, Ada Lovelace is replaced by Megan Smith.
Array people now consists of four elements — “Megan Smith”, “Annie Easley”, “Margaret Hamilton”, and “Frances Spence”.
Exercise 3
On paper, create an array which stores the names of people that inspire you. Then write down two different ways you can access the second-to-last name in your array?
In this case, my people array consists of three names: “Rebecca Sugar”, "Robert Clary" and "Leslie Odom, Jr.".
The second-to-last name is Robert Clary. We can access Robert Clary two ways:
people[-2] (second-to-last position)
people[1] (second position)
Exercise 4
On paper, create an array which stores the numbers 1 – 15. Then write down two different ways of accessing the middle number?
I create an array called num, and assign the result of Array.new(15) { |i| i + 1 } to it.
In this case, I’m telling Ruby: “Hey, I want a new array with 15 elements”. Inside the curly braces (where i equals the current index number), I add 2 to i. This gives me a list of numbers 1 – 15.
In this case, I’m just gonna say the middle number is 8. (That’s what you get when you divide num.length by 2, anyway).
Two different ways of accessing 8:
num[-8]
num[7]
Exercise 5
On paper, write code that will create an array named powers_of_2, and stores the fist 10 powers of 2
I assign the result of Array.new(10) { |i| 2 ** (i + 1) } to the variable powers_of_2.
In this case, I pass 10 as a parameter into Array.new. This tells Ruby: “Hey, I want 10 elements in my new array”.
Next, I use curly braces to populate powers_of_2. With i as the current index number, I give 2 to the power (i + 1) by using the ** operator.
For example: With i as index 0, I give 2 to the power (0 + 1) …or just 1. The result is 2, which becomes my first element.
After the code exectues, powers_of_2 consists of 10 integers: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024
Summary
This lesson introduces nil and arrays.
In Ruby, a variable automatically returns nil when it doesn’t have a value stored inside it.
Arrays are a container of things. They are ideal when managing data that would otherwise require too many variables to manage.
Array indices start at 0 and increase by 1.
Here’s what we can do with arrays:
Create them (with [] or Array.new)
Print them (with puts, print or puts "#{arr}")
Change and access them (by referencing an element’s index)
Add to the end (with .push or shovel)
Iterate over them (with a do ... end block or curly braces)
Cool things I learned in JSL Day 5
In no particular order:
We can pass up to two parameters into Array.new
We can also pass a block as a parameter with curly braces (to populate our new array with particular values)
Curly braces are a great way to execute a single line of code for .each
.each_with_index gives us both the element and its index
JSL Day 4 includes only one exercise: A password verification program!
My program follows this structure:
while loop (continues as long as variable confirmed is false)
until loop (continues until password meets requirements)
Multiple if-statements (tells user what they did wrong)
if-else-statement (user input determines if while loop breaks or continues)
Below is the first part of my program. You can see the following:
Assignment of user input to variable password
Start of the while loop (which runs as long as confirmed evaluates as false)
Entire until loop (which runs until password meets all the requirements)
Let me break this down into smaller parts…
First, user input collection: Outside of any loops, I prompt the user for a password. User input is assigned to variable password.
Next, I set a new variable — confirmed. It’s initialized with a value of false. (This is important for the while loop below it.)
Looping the program
I needed my program to do the following:
Evaluate whether password meets all criteria
Keep prompting user for password until it meets all criteria
Ask user to re-enter their password
Start the whole process over if the re-entered password doesn’t match the original
Simply put, my program’s end goal is: Get the user to enter the same, approved password twice.
My confirmed variable will only switch over to true if the user successfully meets this goal.
That’s why my larger while loop hinges on confirmed — and whether it’s true or false. Basically, I’m telling Ruby: “As long as confirmed stays false, keep running this program.”
Evaluating user input
My first step is evaluating whether password meets all the criteria. (In fact, I actually wrote this part first!)
An until loop keeps running until password has at least:
Eight characters
One number
One symbol
One uppercase letter
One lowercase letter
Because there are so many boolean statements, I indented them for readability.
(Note: I made sure to leave logical operators at the end of each line. Why? So Ruby knows to keep going for more boolean statements.)
How my program evaluated user input:
Does password have at least eight characters?
Evaluates whether password.length is greater than or equal 8.
If it evaluates as true, then password has at least eight characters.
Does password have at least one number?
Uses =~ method on password with /\d/ as the argument. (Found this here.)
If it evaluates as true, then password has at least one number.
Does password have at least one symbol?
Individually evaluates each possible symbol by calling the .include? method on password, and entering its respective symbol as an argument.
I only need one symbol. Or (||) only needs one statement to be true. If one of them is true, then the whole statement to evaluate as true.
When that happens, it means password has at least one symbol.
Does password have at least one uppercase letter?
Uses =~ method on password with /[A-Z]/ as the argument. (Found this here.)
If it evaluates as true, then password has at least one uppercase letter.
Does password have at least one lowercase letter? Evaluates whether password is equal to password.upcase.
If it is NOT equal, then the statement returns true.
(Note: I wanted to use =~ like I did for the uppercase condition…but couldn’t find anything for it. This was the other solution I came up with.)
WHEW! That was a lot to comb through!
Operator “and” (&&) requires ALL adjoining statement to be true. So until the above conditions are ALL true, then the loop will continue prompting the user for another password.
Aaaand here’s what that looks like:
Alright, so what if the user needs to re-enter a password? How will they know what they did wrong?
Through the power of FRIENDSHIP? No! The power of if-statements!
A series of if-statements a) figures out what the user did wrong, and b) prints a message telling them what they need.
Here’s what that looks like:
Why did I use five if-statements? Why not if-elsif-else?
(Geez, does Jansen really love if-statements that much? Close, but not quite!)
Our user will most likely botch up multiple things in their password. In other words, we need the flexibility for multiple conditions to evaluate as true.
In an if-elsif-else (or if-elsif) block? A totally different story.
How come? Because the second our program comes across one true statement, it’ll drop the whole thing like it’s hot!
This behavior is also known as short circuit evaluation.
The if-statements are checking for passwords that do NOT meet the criteria. As a result, my conditions are generally an inversion of my first set of boolean statements.
Here’s an example: Unlike before, we’re not checking if password.length is greater than or equal to 8. Instead, we’re now checking if password.length is less than8.
These if-statements determine which messages (if any) are shown to the user.
After that, the until loop these statements are nested under prompts the user to try again.
New user input is assigned to variable password. The until loop once again checks to see if the new string meets our criteria.
Here’s what that looks like:
Verifying the password
So our password meets all the requirements established in the until loop. Great!
Now we just have to “verify” the password…by prompting the user to enter their password a second time.
If they successfully type the same password a second time, the while loop breaks (and the program ends).
Here’s how that works:
First, we prompt the user to re-enter the password. The input is assigned to a new variable called confirmation.
If confirmation has the same value as password, then it prints a congratulatory message for the user. Variable confirmed switches to true, and the while loop breaks (since it runs as long as confirmed is false).
Otherwise, we tell the user that they didn’t succeed. After prompting them to enter another password, we assign it to variable password.
Our program throws it back to the beginning of the while loop, which kicks it down to the until loop for evaluation. The cycle begins anew!
Here’s what that looks like:
Summary
This exercise let me practice working with nested loops, loops with logical operators, boolean zen, and if-statements.
Cool things I learned from JSL Day 4:
In no particular order:
Boolean zen is another way of saying “writing clean, concise boolean statements”.
A good rule of thumb: Never compare a variable to true or false
Fencepostproblems require an extra line of code outside of the loop
References
How to check if a string has at least one number in Ruby [Stack Overflow]
How to check if a string contains uppercase characters in Ruby [java2s.com]
The code we write is just as important as the way we write it.
Boolean zen describes code using boolean values written in the most concise way possible.
If you are comparing to true or false in an if-statement, while loop or until loop, that’s not boolean zen.
In other words, ==true or ==false false is no good!
Why? We don’t need it! Things will automatically evaluate as true or false without the comparison.
Here are some examples:
We don’t need to write if-statements that print true or false.
Instead, we can just puts the boolean statement itself…since it automatically evaluates as true or false on its own!
The if-else structure is wholly unnecessary.
We don’t need to compare correct to true. That’s because correct by itself will either evaluate to true or false.
This is similar to Example 2, except it uses a while loop.
We still don’t need to compare not_correct to true! That’s because the variable not_correct alone already holds a value of true or false.
This will come up more when I start writing functions.
Advanced Loops
This section explains a little bit about:
Fencepost problems
Loops with logical operators
Fencepost Problems
Imagine building fences with posts and wire.
In fact, let’s draw a picture: |-|-|-|-| with the | representing a fence post and the - representing the wire of the fence
You’ll need one more post than you do wire sections.
Loops follow a similar structure. However, when writing loops, it’s easy to have a bit of unwanted “wire” just hanging off the end.
In programming, this is also known as a “loop-and-a-half”.
A common solution? Make the loop run one fewer times than needed — and handle that last “post” outside of the loop itself.
Here’s an example:
If the user enters 3 as max, we’d want the loop to print 1, 2, 3 — three numbers, but only two commas.
This solution prints the first “fencepost” outside of the loop (with print 1 ).
Inside the loop, we print commas…followed by the next number.
Another example:
You can also do the loop first…and then print the last “fencepost” outside the loop.
Let’s say the user enters 3 as max again.
The loop goes up from 1 to max - 1. That means it will print 1, 2, …and then the loop ends.
But outside the loop, we’ve got print max. That prints 3…and the fence is complete!
Loops with Logical Operators
Let’s take a closer look at using logical operators with loops — specifically, while loops and until loops.
An example:
While the number is:
less than 1 (not greater than 0)
OR greater than 99 (not less than 100)…
We’ll prompt the user to re-enter their number.
We cannot put AND — because it’s not possible for a number to be less than 1 AND greater than 99.
Another example:
Until the number is:
Greater than 0
AND less than 100…
We’ll prompt the user to re-enter their number.
We cannot put OR — because you could enter 101, and 101 is greater than 0. That means 101 would stop the loop, even though it doesn’t meet both requirements.
Summary
This lesson talks about boolean zen and advanced loops!
Boolean zen is another way of saying “writing clean, concise boolean statements”.
Here’s a good rule of thumb: Never compare a variable to true or false.
Why? Because the variables will automatically evaluate to true or false on their own.
When working with advanced loops, we’ll encounter fencepost problems (aka loop-and-a-half).
To avoid unwanted “wire”, we can make the loop run one less round than needed…and insert an additional “fencepost” outside of the loop. We can insert these extra “fenceposts” either before or after the loop in question.
Lastly, we must pay attention to using logical operators inside loops — namely, until and while loops.
Here’s why: Because similar problems will require different operators, depending on how you frame the condition for breaking the loop.
…And that’s all for now! Stay tuned for the Day 4 Exercise. 🙂
Write a program that allows a user to play a guessing number game. Your program should generate a random number between 0 – 1000 (including 0, but not including 1000). Allow the user to make a guess until they guess the answer. After each guess you should print “higher” or “lower”. When they guess it correctly print a winning message along with their total number of guesses.
My solution:
Since 1000 is excluded, I used ... for the range.
(Note: Pretty sure rand(1000) does the same thing, but I wanted to practice the syntax covered in JSL.)
The random number generated by rand() is assigned to rand_number.
User input is converted into an integer and assigned to guess. I initialized a variable named number_of_guesses with a value of 1.
Next, I created a while loop. While guess does NOT equal rand_number, the loop will continuously run.
Inside the loop, an if-statement checks whether guess is higher or lower than rand_number. Depending on which statement is true, the program prints a helpful hint (HIGHER or LOWER) for the user.
After printing the hint, the program collects input from the user again. The new value is stored in guess.
Next, number_of_guesses increments by 1 with a compound assignment statement.
When the user guesses the correct number, they receive a congratulatory message. It tells them how many guesses they made.
Sample run:
Exercise 2
Write a program that plays duck duck goose. Allow the user to enter the player’s number they want to call goose on, and then say “duck” for each player before the “goose”, then say “goose” for the chosen player.
My solution:
First, I print a message for the user. Then, I convert user input to an integer — and assign it to variable goosed_player.
Next, I use range.each for the duck-duck-duck” portion of the game. The range is between 1 and the number assigned to goosed_player. However, ... excludes goosed_player from the loop.
Since each number represents a player, I assign the variable player to the current iterator.
For every player (except the one being Goosed), it prints “Duck”.
The loop stops before it reaches the value assigned to goosed_player. Then, I print a message that “Gooses” whichever player the user selected.
Sample run:
Exercise 3
Write a program that allows a user to enter the number of petals on a flower. Then one by one, print “plucking petal #1: they love me!”. Alternate “They love me” and “They love me not” as well as increase the petal number for each petal.
My solution:
User input is collected, converted to an integer, and then assigned to the variable named petals.
An each loop iterates over a range of 1 to the value stored inside petals. This range is inclusive.
The current iterator is assigned to the variable num.
Inside the loop, an if-statement evaluates what to print. If the current num is odd, it prints “They love me!” If the current num is even, it prints “They love me not.”
The loop ends after it prints the statement associated with the number stored inside petals.
Sample run:
Exercise 4
You don’t trust your users. Modify the program below to require the user to enter the same value twice in order to add that value to the total.
Original code:
My solution:
First, I initialized a third variable named verification — setting it at 0.
Next, I changed the while loop to an until loop.
Why? Personally, until made it easier for me to imagine what conditions to place on the loop — especially since I’m now juggling a second condition.
(Note: I certainly could have used while, but with inverted conditions instead.)
My program checks if input <= -1 and input == verification both evaluate as true.
In other words, I’m telling Ruby: “Until input is -1 or less, AND input equals verification, keep running this loop.”
The loop makes the user give input twice. One value is assigned to input, the other is assigned to verification.
Next, an if-statement evaluates whether to add the user’s number to total.
If input and verification are the same, AND input isn’t a negative number, then input is added to the current value stored inside total.
When the loop ends, the program prints total.
Sample run:
Summary
These exercises let me practice working with scope, loops and iterators. I also used if-elsif-else statements and conditionals.
Cool things I learned from JSL Day 3:
In no particular order:
We can use .times as a regular loop or an iterator (with its own variable assigned between pipes)
.times refers to the number of elements inside the collection.
range.each passes a specific range into the code as a collection. This range can either include or exclude the final value.