Monday, April 11, 2016

Ruby_Lesson_Hashes and Symbols

More on Hashes
1.The Story So Far
We can create hashes several ways, but two of the most popular are hash literal notation:
new_hash = { "one" => 1 }
and hash constructor notation:
new_hash = Hash.new
2.Iterating Over Hashes
matz.each {|x,y| puts y}
3.Nil: a Formal Introduction
What happens if you try to access a key that doesn't exist, though?
In many languages, you'll get an error of some kind. Not so in Ruby: you'll instead get the special value nil.
Along with false, nil is one of two non-true values in Ruby. (Every other object is regarded as "truthy," meaning that if you were to type if 2 or if "bacon", the code in that if statement would be run.)
It's important to realize that false and nil are not the same thing: false means "not true," while nil is Ruby's way of saying "nothing at all."
4.Setting Your Own Default
You don't have to settle for nil as a default value, however. If you create your hash using the Hash.new syntax, you can specify a default like so:
my_hash = Hash.new("Trady Blix")
Now if you try to access a nonexistent key in my_hash, you'll get "Trady Blix" as a result.
You can always read more hashy goodness in the offical Ruby documentation.
The Many Faces of Symbol
6.What's a Symbol?
You can think of a Ruby symbol as a sort of name. It's important to remember that symbols aren't strings:
"string" == :string # false
Above and beyond the different syntax, there's a key behavior of symbols that makes them different from strings: while there can be multiple different strings that all have the same value, there's only one copy of any particular symbol at a given time.
puts "string".object_id
puts "string".object_id
puts :symbol.object_id
puts :symbol.object_id


The .object_id method gets the ID of an object—it's how Ruby knows whether two objects are the exact same object. The two "strings" are actually different objects, whereas the :symbol is the same object listed twice.
7.Symbol Syntax
Symbols always start with a colon (:). They must be valid Ruby variable names, so the first character after the colon has to be a letter or underscore (_); after that, any combination of letters, numbers, and underscores is allowed.
Make sure you don't put any spaces in your symbol name—if you do, Ruby will get confused.
:my symbol # Don't do this!
:my_symbol # Do this instead.
8.What are Symbols Used For?
Symbols pop up in a lot of places in Ruby, but they're primarily used either as hash keys or for referencing method names. Symbols make good hash keys for a few reasons:
  1. They're immutable, meaning they can't be changed once they're created;
  2. Only one copy of any symbol exists at a given time, so they save memory;
  3. Symbol-as-keys are faster than strings-as-keys because of the above two reasons.
9.Converting Between Symbols and Strings
:sasquatch.to_s
# ==> "sasquatch"

"sasquatch".to_sym
# ==> :sasquatch
  1. Create a new variable, symbols, and store an empty array in it.
  2. Use .each to iterate over the strings array.
  3. For each s in strings, use .to_sym to convert s to a symbol and use .push to add that new symbol to symbols.
strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]
symbols = []
strings.each {
    |s|
    symbols.push(s.to_sym)
}

10.Many Paths to the Same Summit
Besides using .to_sym, you can also use .intern. This will internalize the string into a symbol and works just like .to_sym:
"hello".intern
# ==> :hello
The Marriage of Hash and Symbol
11.All Aboard the Hash Rocket!
The hash syntax you've seen so far (with the => symbol between keys and values) is sometimes nicknamed the hash rocket style.
numbers = {
  :one => 1,
  :two => "two",
  :three => 3,
}
12.The Hash Rocket Has Landed
new_hash = { one: 1,
  two: 2,
  three: 3
}
The two changes are:
  1. You put the colon at the end of the symbol, not at the beginning;
  2. You don't need the hash rocket anymore.
It's important to note that even though these keys have colons at the end instead of the beginning, they're still symbols!
13.Dare to Compare
We mentioned that hash lookup is faster with symbol keys than with string keys. Here, we'll prove it!
require 'benchmark'
string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]
string_time = Benchmark.realtime do
  100_000.times { string_AZ["r"] }
end
symbol_time = Benchmark.realtime do
  100_000.times { symbol_AZ[:r] }
end
puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."

14.Becoming More Selective
To filter a hash for values that meet certain criteria, we can use .select.
grades = { alice: 100,
  bob: 92,
  chris: 95,
  dave: 97
}
grades.select {|name, grade| grade < 97}
# ==> {:bob=>92, :chris=>95}
grades.select { |k, v| k == :alice }
# ==> {:alice=>100} 
15.More Methods, More Solutions
Ruby includes two hash methods, .each_key and .each_value, that do exactly what you'd expect:
my_hash = { one: 1, two: 2, three: 3 }
my_hash.each_key { |k| print k, " " }
# ==> one two three
my_hash.each_value { |v| print v, " " }
# ==> 1 2 3
Project: A Box Office Hash 
It'll do one of four things: add a new movie to a hash, update the rating for an existing movie, display the movies and ratings that are already in the hash, or delete a movie from the hash. If it doesn't receive one of those four commands, the program will output some kind of error message.
movies = {
  Memento: 3,
  Primer: 4,
  Ishtar: 1
}

puts "What would you like to do?"
puts "-- Type 'add' to add a movie."
puts "-- Type 'update' to update a movie."
puts "-- Type 'display' to display all movies."
puts "-- Type 'delete' to delete a movie."

choice = gets.chomp.downcase
case choice
when 'add'
  puts "What movie do you want to add?"
  title = gets.chomp
  if movies[title.to_sym].nil?
    puts "What's the rating? (Type a number 0 to 4.)"
    rating = gets.chomp
    movies[title.to_sym] = rating.to_i
    puts "#{title} has been added with a rating of #{rating}."
  else
    puts "That movie already exists! Its rating is #{movies[title.to_sym]}."
  end
when 'update'
  puts "What movie do you want to update?"
  title = gets.chomp
  if movies[title.to_sym].nil?
    puts "Movie not found!"
  else
    puts "What's the new rating? (Type a number 0 to 4.)"
    rating = gets.chomp
    movies[title.to_sym] = rating.to_i
    puts "#{title} has been updated with new rating of #{rating}."
  end
when 'display'
  movies.each do |movie, rating|
    puts "#{movie}: #{rating}"
  end
when 'delete'
  puts "What movie do you want to delete?"
  title = gets.chomp
  if movies[title.to_sym].nil?
    puts "Movie not found!"
  else
    movies.delete(title.to_sym)
    puts "#{title} has been removed."
  end
else
  puts "Sorry, I didn
't understand you."
end

2.Setting Up
First things first: let's create a hash to hold our movies and their ratings, and let's prompt the user for input so we can eventually store movie/ratings pairs in our hash.

  1. A Hash is a way of storing data by a specifiable key, as opposed to an array which can only use numbers. It is created like { } above.
  2. puts asks a question on the command line, here we ask if you like coding in Ruby.
  3. In order to get the user input, we have to call .chomp on gets
movies = {
    love:10,
    war:1,
    family:6
    }
puts "Please make your choice:"
choice=gets.chomp

3.The Case Statement
The case statement, which will decide what actions to take based on what the user types in.The syntax looks like this:
case language
when "JS"
  puts "Websites!"
when "Python"
  puts "Science!"
when "Ruby"
  puts "Web apps!"
else
  puts "I don't know!"
end
The else is what the case statement will do if it doesn't match any of its when statements to the case.
Below your existing code, create a case statement for the choice variable with the following when conditions:
  1. when "add", please puts "Added!"
  2. when "update", please puts "Updated!"
  3. when "display", please puts "Movies!"
  4. when "delete", please puts "Deleted!"
  5. Otherwise (i.e. else), please puts "Error!"
Don't forget the end statement after your case/when lines.
case choice
when "add"
    puts "Added!"
when "update"
    puts "updated!"
when "display"
    puts "Movies!"
when "delete"
    puts "Deleted!"
else
    puts "Erro!"
end

4.Prompting: Redux!

  • Inside your when "add" block, remove the puts "Added!" statement.
  • In its place, prompt the user for a movie title. Save the result in a new variable called title. (*Your code already has an example of how to do this!)
  • Next, prompt the user for the rating of the movie. Save that in a new variable called rating.
  • Add that movie/rating pair to the movies hash and puts a message indicating the pair was added. (No need for to_sym or to_i just yet!)
puts "Input movie"
   title = gets.chomp
   puts "Input rating"
   rating = gets.chomp
   movies[title] = rating
   puts "#{title} with rating #{rating} has been added!"

5.Not My Type
Ruby's .to_sym method can convert a string to a symbol, and .to_i will convert a string to an integer.
Call .to_sym on your title and .to_i on your rating so that your movie titles are stored as symbols in the hash and the associated ratings are stored as integers.
   puts "Input movie"
   title = gets.chomp.to_sym
   puts "Input rating"
   rating = gets.chomp.to_i

6.Error! Error!
The final thing we'll want to do is perform a check to see whether the movie to be added is already in the hash.Add an if/else statement to the add branch of your case. If the movie isn't already in the hash (that is, if movies[title.to_sym] is nil), it should add the movie/rating pair; otherwise, it should puts that the movie already exists and not add anything to the hash. Make sure to test it!
   if movies[title.to_sym].nil?
        puts "Input rating"
        rating = gets.chomp
        movies[title.to_sym] = rating.to_i
        puts "#{title} with rating #{rating} has been added!"
    else
        puts "already exists!"

7.Update
  1. Inside your when "update" block, remove the puts "Updated!" statement.
  2. Prompt the user for a movie title. Store it in title.
  3. if the movies[title] is nil, then the movie is not in the hash. Please puts a string telling the user of their error.
  4. Otherwise (else), we need to update the movies hash. Prompt the user for a new rating. Set the movie's rating to that new value.
  puts "Input movie"
    title = gets.chomp
    if movies[title.to_sym].nil?
        puts "does not exists!"
    else
        puts "Input new rating"
        rate = gets.chomp
        movies[title.to_sym] = rate.to_i
    end

8.Display
  1. First, remove the puts "Movies!" when the user types "display".
  2. Next, iterate through the hash using .each and puts each movie/rating pair. The format should be "#{movie}: #{rating}.
 movies.each {
        |movie,rate| puts "#{movie}: #{rate}"
        }

9.Delete
Ruby makes it easy to remove a movie/rating pair from our hash: we just write movies.delete(title)!
  1. Go ahead and remove the puts "Deleted!" when the user types "delete".
  2. Get the title from the user.
  3. Include an if/else statement that puts an error if the movie's not in the hash; if it's there, use .delete to remove it as shown above.
    puts "Input title"
    title = gets.chomp
    if movies[title.to_sym].nil?
        puts "not exist"
    else
        movies.delete(title.to_sym)
    end






















No comments :

Post a Comment