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
.each
to iterate over thestrings
array. - For each
s
instrings
, use.to_sym
to converts
to a symbol and use.push
to 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. puts
asks 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
.chomp
ongets
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
movies
hash andputs
a message indicating the pair was added. (No need forto_sym
orto_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
- Inside your
when "update"
block, remove theputs "Updated!"
statement. - Prompt the user for a movie title. Store it in
title
. if
themovies[title]
isnil
, then the movie is not in the hash. Pleaseputs
a string telling the user of their error.- Otherwise (
else
), we need to update themovies
hash. 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
.each
andputs
each 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
/else
statement thatputs
an error if the movie's not in the hash; if it's there, use.delete
to 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