Skip to content

Instantly share code, notes, and snippets.

@pinkopaque22
Created December 18, 2015 00:21
Show Gist options
  • Save pinkopaque22/cc4f94bd23dc33d41257 to your computer and use it in GitHub Desktop.
Save pinkopaque22/cc4f94bd23dc33d41257 to your computer and use it in GitHub Desktop.
Hashes in Ruby-Exercises
__Details__
This exercise will help you get familiar with some common Hash methods that Ruby provides.
merge_us method
Create a method named merge_us that expects two arguments and combines them, assuming they're hashes. The Hash class has a method named merge that will be helpful to you.
my_keys method
Create a method named my_keys that takes a hash argument and returns an Array of its keys. The Hash class has a method named keys that will be helpful to you.
do_i_have? method
Create a method named do_i_have? which expects two arguments. The arguments should represent a Hash and Array of keys. do_i_have? should return true if the Array has all of the keys in the hash, and false if it does not. To create this method, you could start with something like this:
def do_i_have?(hash, array_of_keys)
end
You want to make sure that every key in the hash argument is also contained in the array_of_keys argument. (Remember that hash is expected to be Hash and array_of_keys is expected to be an Array). To compare two objects for equality, you need them to be evaluated as the same object type. That is, you can't explicitly compare a Hash to an Array, but you can compare an Array to an Array. The first thing you can do is to return an array of keys from the hash argument:
def do_i_have?(hash, array_of_keys)
hash.keys #=> returns an array of keys
end
We can now compare two arrays - the array returned from hash.keys and the array_of_keys array that was passed as an argument:
def do_i_have?(hash, array_of_keys)
hash.keys == array_of_keys
end
This is an improvement, but unfortunately it won't satisfy all of the specs. Our method should return true if all of the elements in the array_of_keys array are within the set of keys in the hash argument, without regard to a specific order. Our method above compares the two arrays with regard to order. Try using the sort method provided by Ruby to order both arrays in the same fashion. Once they are ordered the same, you can do a simple comparison to determine equality.
Read the Ruby Docs and learn all of the methods that Ruby provides for the Hash class.
__Specs__
describe "merge_us" do
it "merges two hashes that are unique" do
h1 = { name: "Computer", cost: "$1,000" }
h2 = { first_name: "Bob", age: 34 }
new_hash = { name: "Computer", cost: "$1,000", first_name: "Bob", age: 34 }
expect( merge_us(h1, h2) ).to eq(new_hash)
end
it "merges two hashes that have overlapping sets" do
h1 = { name: "Computer", cost: "$1,000" }
h2 = { name: "Mouse", uuid: "1234" }
new_hash = { name: "Mouse", cost: "$1,000", uuid: "1234" }
expect( merge_us(h1, h2) ).to eq(new_hash)
end
end
describe "my_keys" do
it "returns keys for a small hash" do
h = { name: "Computer", cost: "$1,000" }
keys = [:name, :cost]
expect( my_keys(h) ).to eq(keys)
end
it "returns keys for a larger hash" do
h = { name: "Mouse", cost: "$5", uuid: "1234" }
keys = [:name, :cost, :uuid]
expect( my_keys(h) ).to eq(keys)
end
end
describe "do_i_have?" do
it "returns false if it doesn't have any of the keys" do
h = { name: "Computer", cost: "$1,000" }
keys = [:age, :bio]
expect( do_i_have?(h, keys) ).to eq(false)
end
it "returns false if one or more of the keys isn't in the hash" do
h = { name: "Computer", cost: "$1,000" }
keys = [:name, :bio, :cost]
expect( do_i_have?(h, keys) ).to eq(false)
end
it "returns false if the hash has a different number of keys than the array" do
h = { name: "Computer", cost: "$1,000" }
keys = [:name]
expect( do_i_have?(h, keys) ).to eq(false)
end
it "returns true if all keys are in the hash" do
h = { name: "Computer", cost: "$1,000", uuid: "1234" }
keys = [:name, :cost, :uuid]
expect( do_i_have?(h, keys) ).to eq(true)
end
it "returns true if all keys are in the hash, regardless of order" do
h = { name: "Computer", cost: "$1,000", uuid: "1234" }
keys = [:name, :uuid, :cost]
expect( do_i_have?(h, keys) ).to eq(true)
end
end
__Code__
def merge_us(h1, h2)
h1.merge!(h2) #{ |key, v1, v2| v1 } this bit is from ruby docs but not necessary for these specs
end
def my_keys(h)
h.keys
end
def do_i_have?(h, array_of_keys)
if h.keys.sort == array_of_keys.sort
true
else
false
end
end
__Details__
Just like Array, the Hash class has an each instance method. Unlike Array#each, this each method takes two block arguments (block arguments are in between the "pipes" (|a,b|)). The arguments represent the key and the value respectively.
Create a method named hash_to_array that takes a Hash argument and returns an Array with the keys and values displayed as strings:
"#{key} is #{value}"
You can start by defining the method, stating the argument, and creating a return variable:
def hash_to_array(hash = {})
a = []
end
Just like we mentioned in the requirements, we defined a method named hash_to_array, declared a hash argument defaulted to a Hash, and also created a variable a, which will be the Array that is returned from the method.
To iterate over the Hash argument, you could write:
def hash_to_array(hash = {})
a = []
hash.each do |key, value|
# build the "a" array here
end
# return the "a" array
end
During each iteration in the each block, we should use interpolation to create a string containing the key and value:
"#{key} is #{value}"
Then we should add that string to the a Array, using the "shovel" (<<) syntax.
Finish writing the hash_to_array method so that it satisfies the specs.
Read the Hash#each method.
__Specs__
describe "hash_to_array" do
it "returns array for short hash" do
hash = { name: "Bob", age: 34 }
array = [ "name is Bob", "age is 34" ]
expect( hash_to_array(hash) ).to eq(array)
end
it "returns array for longer hash" do
hash = { name: "Joe", age: 34, sex: :male }
array = [ "name is Joe", "age is 34", "sex is male" ]
expect( hash_to_array(hash) ).to eq(array)
end
end
__Code__
def hash_to_array(hash = {})
a = []
hash.each do |key, value|
a << "#{key} is #{value}"
end
a
end
__Details__
Create a class named User. Its initialize method should take a Hash as an argument. We'll name the argument config and set a default value for the argument to an empty Hash:
class User
def initialize(config = {})
end
end
This config = {} syntax supplies a "default argument" for initialize. If someone initializes a User instance without a config argument, the config variable in the method will be automatically set to the default we gave it -- an empty Hash.
The config argument should be used to set any of the following attributes on a user: name, email, bio, age, and sex. If an attribute is not provided in the Hash argument, the initialize method should default it to a value to "n/a". For example:
class User
def initialize(config = {})
@name = config[:name] || "n/a"
@email = config[:email] || "n/a"
...
end
end
Setting default values is a very common task in Ruby. A basic way to do this is with the || assignment operator, which you'll remember means "or". Consider the following examples:
a = 3
a = a || 6
a #=> 3
b = b || 9
b #=> 9
See if you can decode the logic in the conditional assignments above.
We'll also need to access the instance variables set in our initialize method. We can use the handy attr_accessor method declaration to do this. The attr_accessor method also lets you declare multiple attributes on one line. For example:
class User
attr_accessor :name, :email
def initialize(config = {})
@name = config[:name] || "n/a"
@email = config[:email] || "n/a"
# ...
end
end
Finish writing the User class and initialize method to handle all of the required attributes.
__Specs__
describe "User" do
describe "initialize" do
it "can set just the name" do
u = User.new(name: "Bob")
expect( u.name ).to eq("Bob")
expect( u.email ).to eq("n/a")
end
it "can set all values" do
u = User.new(name: "Joe", email: "joe@example.com", bio: "Cool dude", age: 34, sex: :male)
expect( u.name ).to eq("Joe")
expect( u.email ).to eq("joe@example.com")
expect( u.bio ).to eq("Cool dude")
expect( u.age ).to eq(34)
expect( u.sex ).to eq(:male)
end
it "can set no values" do
u = User.new
expect( u.name ).to eq("n/a")
expect( u.email ).to eq("n/a")
expect( u.bio ).to eq("n/a")
expect( u.age ).to eq("n/a")
expect( u.sex ).to eq("n/a")
end
end
end
__Code__
class User
attr_accessor :name, :email, :bio, :age, :sex
def initialize(config = {})
@name = config[:name] || "n/a"
@email = config[:email] || "n/a"
@bio = config[:bio] || "n/a"
@age = config[:age] || "n/a"
@sex = config[:sex] || "n/a"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment