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 Hashesmatz.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
Along with
It's important to realize that
4.Setting Your Own DefaultIn 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."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 :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 (
Make sure you don't put any spaces in your symbol name—if you do, Ruby will get confused.
:). 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?
- They're immutable, meaning they can't be changed once they're created;
- Only one copy of any symbol exists at a given time, so they save memory;
- Symbol-as-keys are faster than strings-as-keys because of the above two reasons.
:sasquatch.to_s
# ==> "sasquatch"
"sasquatch".to_sym
# ==> :sasquatch
- Create a new variable,
symbols, and store an empty array in it. - Use
.eachto iterate over thestringsarray. - For each
sinstrings, use.to_symto convertsto a symbol and use.pushto add that new symbol tosymbols.
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 Symbol11.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 Landednew_hash = { one: 1,
two: 2,
three: 3
}
The two changes are:- You put the colon at the end of the symbol, not at the beginning;
- You don't need the hash rocket anymore.
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 SolutionsRuby 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.
- 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. putsasks a question on the command line, here we ask if you like coding in Ruby.- In order to get the user input, we have to call
.chompongets
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:when "add", pleaseputs "Added!"when "update", pleaseputs "Updated!"when "display", pleaseputs "Movies!"when "delete", pleaseputs "Deleted!"- Otherwise (i.e.
else), pleaseputs "Error!"
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 theputs "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
movieshash andputsa message indicating the pair was added. (No need forto_symorto_ijust 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
- Inside your
when "update"block, remove theputs "Updated!"statement. - Prompt the user for a movie title. Store it in
title. ifthemovies[title]isnil, then the movie is not in the hash. Pleaseputsa string telling the user of their error.- Otherwise (
else), we need to update themovieshash. Prompt the user for a new rating. Set the movie's rating to that new value.
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
- First, remove the
puts "Movies!"when the user types "display". - Next, iterate through the hash using
.eachandputseach movie/rating pair. The format should be"#{movie}: #{rating}.
|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)!- Go ahead and remove the
puts "Deleted!"when the user types "delete". - Get the title from the user.
- Include an
if/elsestatement thatputsan error if the movie's not in the hash; if it's there, use.deleteto remove it as shown above.
title = gets.chomp
if movies[title.to_sym].nil?
puts "not exist"
else
movies.delete(title.to_sym)
end
No comments :
Post a Comment