Contact List Program

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.

Sample Run

This program:

  • Continuously prompts the user for contact names
  • For each contact, continuously prompts the user for phone numbers
  • Prints a list of each contact and their number(s)

More info below….

About the data structure

Here’s the main data structure I used:

  • An array
    • Hashes (for each contact)
      • An array in each hash (for multiple phone numbers)

What does that look like?  Let’s take a look at an example….

contact_list = [ {name: "Karen", phone_number: [123, 456]}, {name: "Maria", phone_number: [789]}, {name: "Elli", phone_number: []} ]

Why is each contact stored inside a hash?  Because hashes use keys to describe the values stored inside.  That means we can be more descriptive with our data.

However, each key must be unique.  If they’re not, we’ll overwrite our existing key-value pairs.

But here’s a handy workaround: Make multiple hashes!

If each hash represents each contact, we can easily store their name and phone numbers…without accidentally erasing other contacts’s info!  (NICE.)

Why is there an array in each contact hash? Because we’ve given the user the option to store multiple numbers.  Since the array is connected to the key “phone_numbers”, we know each element is…you guessed it, a phone number!

Why are these contact hashes stored inside an array?  Well, we need a place to store all these hashes, don’t we?  That’s where our main array comes into the picture!

Elements inside an array are organized according to their index.  Unlike with hashes, you can count (ayyyyy!) on the data to remain in a certain order.

In this case, we get the descriptive goodness of hashes, with the orderly index numbers of an array.  (Plus, we don’t have to worry about overwriting any key-value pairs!)

Methods to the Madness

I used two methods!

Here’s the first:

Method 1
Defining an “ask” method.  Collects and returns user input; converts numbers to integers.

My ask method takes two arguments: question, and the kind of answer (which defaults to a string).

Here’s what it does:

  • Prints question (plus an empty space)
  • Collects input and assigns it to answer
  • Converts answer to an integer if kind is "number"
  • Returns answer

In the past, Ruby has automatically returned answer by default.  In this case, though, I needed to include return answer at the bottom.

What do I mean?  Well…

No Return
I commented out “return answer” to see what happens.

I called the ask method, and assigned the result to name.  But when I try to print name

Nothing happens!

No Return Output
What’s my “name” again?

In other words, I can’t access the value collected by the ask method.  Not unless I specifically tell Ruby to return it, anyway.

Like so:

Return
I put “return answer” back into the method.

Like magic, we can print name now!

Return Output
We returned “name”!

For simpler ask methods I’ve written, I didn’t NEED return answer.

So why now?  Good question!  I wasn’t 100% sure either…but here’s my first guess:

Remember that if-statement we put in there?  That’s the last thing Ruby reads when executing ask.

If answer isn’t a number, then Ruby leaves answer alone.  That means Ruby won’t return it, either.

Let’s disable return answer again — and see what happens if our if-statement evaluates as true

 

No Return Number
Collecting a number…without telling Ruby to return answer.

Sure enough, we get 12…

No Return Number Output

And it’s an Integer to boot!  (NICE.)

My takeaway from this:  In Ruby, a method automatically returns the last value assigned to a variable — IF that’s the last thing the method does.  Otherwise, we basically need to “remind” Ruby to return answer.

Okay, tangent done!  Now we’re off to my second method:

Method 2
Defining an “add_contact” method.  It collects contact info from user and returns a hash.

My add_contact method does the following:

  • Collects contact name and number(s) from the user
  • Returns contact hash

First, I create my hash with two key-value pairs.  (The values are currently empty.)  I assign it to the variable contact.

Next, what’s our contact’s name?  I call the handy ask method, and assign the result to contact[:name].

Now let’s collect some numbers!

A while loop does this, only breaking once answer equals "n".  If answer equals "y", then the method…

  • Assigns user input to variable phone
  • Appends phone to contact[:phone_number] (with .push)

(Before breaking the loop, I remind Ruby to return our resulting hash with return contact.)

WHEW!  Now that we’ve got our methods, how about making that contact list??

The rest of the code can be broken into two loops:

  • A while loop (for collecting contact info from user)
  • An .each loop (for printing the contact list)

Here’s the first loop!

Part 1
A “while” loop that collects and appends user input to “contact_list” array.

First,  I assigned an empty array to contact_list.  I also initialized the variable answer for the while loop.

As long as answer doesn’t equal "n", then the while loop does the following:

  • Execute add_contact method
  • Appends the result to the contact_list array (with .push)
  • Executes ask method
  • Assigns the result to answer

If answer equals "n", then the while loop breaks.

Now, for part two:  Printing the list with .each!

Part 2
Printing a list with nested .each loops

I call .each on the contact_list array.  Each hash inside contact_list is represented by the variable contact.

For each contact, I print the value connected to :name.

Next, an if-statement evaluates how many elements are inside the array connected to :phone_numbers.

If there’s at least 1 element inside the array, then we call .each on contact[:phone_numbers].  The variable phone_numbers represents each element inside the array.  The loop iterates over each phone number, printing it to the screen.

We repeat this process for every hash inside the main contact_list array.

And we’re done!  Let’s take one more look at sample run.

Sample Run

Summary

I created a simple contact list covered in the Ruby loops module of Treehouse.

This program can store:

  • The names and numbers for multiple people (using hashes inside an array)
  • Multiple phone numbers for a single contact (using an array inside each hash)

I practiced the following things:

  • Defining and working with methods
  • Working with
    • Hashes inside arrays
    • Arrays inside hashes
  • Nesting .each loops

Something cool I learned:

  • Ruby automatically returns a value assigned to a variable — unless it executes more code afterward (see both methods above).
  • Defining an argument inside a method means that becomes the default argument.

Leave a comment