Giter Club home page Giter Club logo

nested-hash-iteration-code-along's Introduction

Code Along: Manipulating Nested Hashes

Objectives

  1. Iterate through a nested hash
  2. Modify the correct element in a nested hash

Why Nested Hashes Matter

So much of what we do in programming involves storing data in hashes. Often the hashes that we will encounter will have more than one level. As we get into the web, this will become abundantly clear. To build programs in the future, we'll absolutely need to get comfortable working with hashes. Let's get started!

Code Along Exercise

Fork and clone this lab. You'll be coding your solution in lib/contacts.rb. You'll be manipulating the following Hash:

contacts = {
  "Jon Snow" => {
    name: "Jon",
    email: "[email protected]",
    favorite_ice_cream_flavors: ["chocolate", "vanilla"]
  },
  "Freddy Mercury" => {
    name: "Freddy",
    email: "[email protected]",
    favorite_ice_cream_flavors: ["strawberry", "cookie dough", "mint chip"]
  }
}

Your good buddy Freddy Mercury has recently developed a strawberry allergy! You need to delete "strawberry" from his list of favorite ice cream flavors in the remove_strawberry method.

Iterate over the contacts hash and when you reach the key :favorite_ice_cream_flavors, remove "strawberry" from the Array of Freddy's favorite ice cream flavors.

There are at least two ways you can accomplish this, and for this codealong, we'll work with the second way.

  1. You can directly iterate over the hash that is the value of the "Freddy Mercury" key by calling an enumerator method in contacts["Freddy Mercury"].

  2. You can set a conditional to iterate through the hash for Freddy Mercury only; when you reach the appropriate level, check to see if the key == ("is equal to") :favorite_ice_cream_flavors. If it is, check to see if the array of flavors contains "strawberry". If it does, then delete it from the Array.

Step 1: Iterate over the first level

Inside the remove_strawberry method, let's take our first dive into the contacts Hash. Then we'll use binding.pry to see where we are.

We are going to first iterate over the top level of the Hash where the keys should be the person and the values should be a Hash of details about the person.

Note on variable naming: This process will be remarkably easier if you name your variables to accurately reflect the data they represent. For now, when the value we're iterating over is another hash, we will explicitly add a _hash to the end of the variable name (E.G. contact_details_hash below).

contacts.each do |person, contact_details_hash|
  binding.pry
end

We can enter the pry in one of two ways: by running learn test or by running ruby lib/contacts.rb. We'll use learn test.

Let's run learn test in the terminal and, at the pry prompt, check that our defined variables (person and contact_details_hash) match our expectations.

person
# => "Jon Snow"

contact_details_hash
# => {:name=>"Jon", :email=>"[email protected]", :favorite_ice_cream_flavors=>["chocolate", "vanilla"]}

Excellent! They do!

Type exit while in pry to continue. The pry should trigger a second time because we have two contacts. You can verify that we're in the second loop through our hash by checking the values of person and data at the pry prompt.

Typing exit now will end the loop and exit pry since we've finished iterating through our contacts. It will also display the results of the test, which we haven't passed just yet.

Step 2. Iterate over the second level

contacts.each do |person, contact_details_hash|
  if person == "Freddy Mercury"
    contact_details_hash.each do |attribute, data|
      binding.pry
    end
  end
end

Again, let's jump into our binding.pry using learn test. We can verify that we've found the record for Freddy Mercury by checking the values of our variables:

attribute
# => :name

data
# => "Freddy"

Before we move on, you will need to exit pry again so you can see the results of the new code we'll be writing in Step 3. We are now inside the loop through the attributes. Because there are three of them, we will need to run exit three times to finish the loop and exit pry. Alternatively, you can run exit! or !!! at any time to exit out of pry entirely.

Step 3. Locate the element we're looking for

contacts.each do |person, contact_details_hash|
  if person == "Freddy Mercury"
    contact_details_hash.each do |attribute, data|
      if attribute == :favorite_ice_cream_flavors
        binding.pry
      end
    end
  end
end

This time we are still iterating through the attributes but we've added a conditional so the pry will only hit when the attribute is equal to :favorite_ice_cream_flavors. If we check the value of data in our binding, we should see the array containing Freddy's favorite flavors.

Step 4. Update the hash

Lastly, we will use delete_if to iterate through the ice cream array and remove any element that matches "strawberry". Recall that data is the array containing Freddy's favorite ice cream flavors. delete_if will iterate through the array, check each element to see if it is equal to "strawberry", and delete the key/value pair if the block returns true. Learn more about it in the ruby docs..

contacts.each do |person, contact_details_hash|
  if person == "Freddy Mercury"
    contact_details_hash.each do |attribute, data|
      if attribute == :favorite_ice_cream_flavors
        data.delete_if {|ice_cream| ice_cream == "strawberry"}
      end
    end
  end
end

The full method should now be:

def remove_strawberry(contacts)
  contacts.each do |person, contact_details_hash|
    if person == "Freddy Mercury"
      contact_details_hash.each do |attribute, data|
        if attribute == :favorite_ice_cream_flavors
          data.delete_if {|ice_cream| ice_cream == "strawberry"}
        end
      end
    end
  end
end

Congrats! You made it. Test that your method works by running ruby bin/contacts in the terminal. It should output the hash without strawberry ice cream. Also, be sure to run the specs to make sure they pass.

nested-hash-iteration-code-along's People

Contributors

annjohn avatar aturkewi avatar curiositypaths avatar curlywallst avatar ihollander avatar jmburges avatar joshuabamboo avatar lizbur10 avatar maxwellbenton avatar msuzoagu avatar sgharms avatar sophiedebenedetto avatar timothylevi avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nested-hash-iteration-code-along's Issues

favorite ice cream flavors symbol discrepancy

Hi guys, I just wanted to mention that as it comes forked from github, the contacts.rb file and the bin/contacts file both have the favorite ice cream flavor symbol entered as favorite_icecream_flavors: while the contacts_spec.rb file has it defined as favorite_ice_cream_flavors:

Thanks!
Kathleen

Let me know if this was one of those things we were supposed to figure out on our own...

There's a typo at the end of "Step 1".

Hello! At the end of "Step 1" in the README, this is what is written:

"You can also run ruby lib/contacts in the terminal - instead of displaying the the test results, this will puts the results of the remove_strawberry method."

The part that says "ruby lib/contacts" should say "ruby bin/contacts". If you run "ruby lib/contacts" in the terminal, you will get this error: "ruby: No such file or directory -- lib/contacts (LoadError)"

I know it's a small issue, but thanks for looking into it.

Sdcrouse

the binding.pry should return "Freddy", not "Jon".

We're told that this:

contacts.each do |person, contact_details_hash|
  if person == "Freddy Mercury"
    contact_details_hash.each do |attribute, data|
      binding.pry
    end
  end
end

will result in this:

> attribute
=> :name
 
> data
=> "Jon"

But because the pry is in a conditional statement that is only true of the person == "Freddy Mecury", data should return "Freddy" instead.

Step 1, command calls no argument.

Step 1 asks students to run the 'ruby bin/contacts' to hit the pry in the code. The problem is that neither 'bin/contacts' or its required '../lib/contacts' have argument to pass to #remove_strawberry(contacts). This results in an error:

...../nested-hash-iteration-code-along-v-000/lib/contacts.rb:16:in `remove_strawberry': wrong number of arguments (0 for 1) (ArgumentError)
    from bin/contacts:3:in `<main>'

This can be corrected by asking students to run 'rspec spec/contacts_spec.rb', or 'learn' as these will run the test suite, which does pass an argument.

Solution appears to be incorrect

The solution for the #remove_strawberry method which is proposed in this code-along appears to be incorrect. It removes the flavor "strawberry" from the favorite_ice_cream_flavors attribute belonging to ANY contact in the hash. The stated intention is to only remove "strawberry" from within the "Freddy Mercury" contact entry. While a more selective iteration can certainly be used (since it seems to be the point of the lesson), a more direct solution is:
contacts["Freddy Mercury"][:favorite_ice_cream_flavors].delete_if{|flavor| flavor == "strawberry"}

To stick with the more comprehensively iterative solution, we could put in a check to be sure we are only accessing the "Freddy Mercury" contact:

  contacts.each do |person, contact_details_hash|
    if person == "Freddy Mercury"
      contact_details_hash.each do |attribute, data|
        if attribute == :favorite_ice_cream_flavors
          data.delete_if {|ice_cream| ice_cream == "strawberry"}
        end
      end
    end
  end

There is a typo at the end of "Step 1" in the README.

In the README for this lab, the README mentions at the end of Step 1 that "you can also run ruby lib/contacts in the terminal - instead of displaying the the test results, this will reach the binding.pry".

I ran "ruby lib/contacts" in the terminal just like the instructions said, but I got this error:
ruby: No such file or directory -- lib/contacts (LoadError)

If I run "ruby lib/contacts.rb" or "ruby bin/contacts", everything works just fine, but "ruby lib/contacts" does not work.

I know that this is a small issue, but the README needs to say "ruby lib/contacts.rb" or "ruby bin/contacts" instead of "ruby lib/contacts.rb". I should also note that if "ruby lib/contacts.rb" is run, the only way that it will reach the binding.pry statement is if the lib/contacts.rb file itself includes a contacts hash and a call to #remove_strawberry. Otherwise, it will output nothing.

Thanks again for looking into this!

Sdcrouse

P.S. Here are a couple of screenshots to add some clarity. This shows the output from running "ruby lib/contacts" and the output from running "ruby lib/contacts.rb" (with the contacts hash and the call to #remove_strawberry inside the file):

nested hash iteration codealong screenshot

Here is the output from running "ruby lib/contacts.rb" WITHOUT including the contacts hash or the call to #remove_strawberry in the lib/contacts.rb file. This also shows the output of running "ruby bin/contacts":

nested hash iteration codealong screenshot 2

Misleading instructions in step 1 of code along

The Lesson states that we need to drop into our pry when testing to see if contacts pushes the correct values within the terminal/pry. The lesson states to run ruby bin/contacts when I really needed to just run learn since the spec file is already accounting that there will be a binding.pry used within the code.

Typo in comment - should be favorite_ice_cream_flavors

In contacts.rb:

  # This is the array we will be passing into the remove_strawberry method
  # contacts = {
  #   "Jon Snow" => {
  #     name: "Jon",
  #     email: "[email protected]", 
  #     favorite_icecream_flavors: ["chocolate", "vanilla"]
  #   },
  #   "Freddy Mercury" => {
  #     name: "Freddy",
  #     email: "[email protected]",
  #     favorite_icecream_flavors: ["strawberry", "cookie dough", "mint chip"]
  #   }
  # }

But the name of the key in contacts_spec.rb is :favorite_ice_cream_flavors, with an extra underscore.

running ruby bin/keys raises errors

When you run ruby bin/keys or any of the other files in /bin it raises an wrong number or arguments error (0 for 1)

In the lesson it tells you to run the commands and look at the output, which doesn't work.

Is this missing first level labels?

Is this missing first level labels? I was having a hard time trying to figure out where the labels "person" and "contact_details_hash" were in this lab.

nested-hash-iteration

the test checks for "favorite_ice_cream_flavors," but the contacts declaration has "favorite_icecream_flavors."

Misaligned test spec and examples.

contacts_spec.rb line 23:
expect(result["Freddy Mercury"][:favorite_icecream_flavors]).to_not include("strawberry")

Currently, it's calling ice_cream. This is not the same key.

Rspec not working with supplied code

Since this is a code-along, shouldn't the code supplied in bin/contacts, on the readme, and in the commented out portion of lib/contacts.rb all correspond? Edit of icecream to ice_cream causes rspec to fail - is this intentional?

Incorrect

I believe this is incorrect. Step two shows a different outcome than the one you should anticipate if you follow the steps. It should return "Freddy," not "Jon."

Step 2. Iterate over the second level
contacts.each do |person, contact_details_hash|
if person == "Freddy Mercury"
contact_details_hash.each do |attribute, data|
binding.pry
end
end
end
Again, let's jump into our binding.pry using learn. You should see:

attribute
=> :name

data
=> "Jon"

Incorrect description?

Where instructions say "There are at least two ways you can accomplish this, and for this codealong, we'll work with the second way." - aren't we then being directed to code the first way?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.