I have a dictionary class and want to be able to push keys (as keywords) and values (as definitions) into an empty hash with an 'add' method. I don't understand how to syntactically write that. I have included an RSPEC file too.
ruby:
class Dictionary
attr_accessor :keyword, :definition
def entries
#hash = {}
end
def add(options)
options.map do |keyword, definition|
#hash[keyword.to_sym] = definition
end
end
end
Rspec:
require 'dictionary'
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can add whole entries with keyword and definition' do
#d.add('fish' => 'aquatic animal')
#d.entries.should == {'fish' => 'aquatic animal'}
#d.keywords.should == ['fish']
end
Any help is appreciated. Thank you!
UPDATE:
Thank you Dave Newton for replying. I used your code and got this error:
ERROR:
*Failure/Error: #d.keywords.should == ['fish']
NoMethodError:
undefined method `keywords' for #<Dictionary:0x007fb0c31bd458
#hash={"fish"=>"aquatic animal"}>*
I get a different error when I convert 'word' into a symbol using #hash[word.to_sym] = defintion
*Failure/Error: #d.entries.should == {'fish' => 'aquatic animal'}
expected: {"fish"=>"aquatic animal"}
got: {:fish=>"aquatic animal"} (using ==)
Diff:
## -1,2 +1,2 ##
-"fish" => "aquatic animal"
+:fish => "aquatic animal"*
Instantiate your hash in Dictionary's initialize:
class Dictionary
def initialize
#hash = {}
end
def add(defs)
defs.each do |word, definition|
#hash[word] = definition
end
end
end
Right now you have no hash until you call entries, which you don't.
entries should return the existing hash, not create a new one.
keywords should return the hash's keys.
You do not need accessors for keyword and definition. Such singular items are meaningless in a dictionary class. You might want something like lookup(word) that returned a definition.
Also, you convert words to symbols, but I don't know why–particularly since you use string keys in your spec. Pick one, although I'm not convinced this is a case where symbols add value.
Please consider variable naming carefully to provide as much context as possible.
Looking at your Rspec, It looks like you need this setup.
class Dictionary
def initialize
#hash = {}
end
def add(key_value)
key_value.each do |key, value|
#hash[key] = value
end
end
def entries
#hash
end
def keywords
#hash.keys
end
end
Do not use key.to_sym in add method, just key
To give flexibility to add method, I can return the self object to continuesly add.
def add(key_value)
key_value.each do |key, value|
#hash[key] = value
end
self
end
So, I can do this now.
#d.add("a" => "apple").add("b" => "bat")
Related
The method "find" seems to work when I pull it out of the Class and test it, but for some reason it is returning empty when it is inside the Class. I can't figure out why...
class Dictionary
def entries
#entries ||= {}
end
def add(entry)
if entry.is_a?(String) == true
#entries = {entry => nil}
else
#entries= entry
end
end
def keywords
#entries.keys.sort
end
def include?(word)
entries.keys.include?(word)
end
def find(word)
result = {}
entries.each_pair do |key, value|
if key =~ /#{word}/
result[key] = value
end
end
result
end
end
It gets stuck at this part of the spec...
it 'finds multiple matches from a prefix and returns the entire entry (keyword + definition)' do
#d.add('fish' => 'aquatic animal')
#d.add('fiend' => 'wicked person')
#d.add('great' => 'remarkable')
#d.find('fi').should == {'fish' => 'aquatic animal', 'fiend' => 'wicked person'}
end
The error says...
expected: {"fish" => "aquatic animal", "fiend" => "wicked person"}
got: {} (using ==)
Diff: ## -1, 3, +1 ##
-"fiend" => "wicked person"
-"fish" => "aquatic animal"
# ./11_dictionary/dictionary_spec.rb:67:in 'block (2 levels) in >'
Your add method replaces the entire entries hash, rather than actually adding an entry. Fix that, and the find method should work.
For completeness, here's how I would implement add:
def add(entry)
if entry.is_a?(Hash)
#entries.merge!(entry)
else
#entries[entry] = nil
end
end
I'm working through the learn ruby tutorials and I'm trying pass the last example where it tests the printable method. I tested the method by calling the method directly within my ruby program and it spits out exactly whats needed. What is preventing my code from properly passing? Any help is greatly appreciated.
Here's the rspec file:
require 'dictionary'
describe Dictionary do
before do
#d = Dictionary.new
end
it 'is empty when created' do
#d.entries.should == {}
end
it 'can add whole entries with keyword and definition' do
#d.add('fish' => 'aquatic animal')
#d.entries.should == {'fish' => 'aquatic animal'}
#d.keywords.should == ['fish']
end
it 'add keywords (without definition)' do
#d.add('fish')
#d.entries.should == {'fish' => nil}
#d.keywords.should == ['fish']
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
it "doesn't cheat when checking whether a given keyword exists" do
#d.include?('fish').should be_false # if the method is empty, this test passes with nil returned
#d.add('fish')
#d.include?('fish').should be_true # confirms that it actually checks
#d.include?('bird').should be_false # confirms not always returning true after add
end
it "doesn't include a prefix that wasn't added as a word in and of itself" do
#d.add('fish')
#d.include?('fi').should be_false
end
it "doesn't find a word in empty dictionary" do
#d.find('fi').should be_empty # {}
end
it 'finds nothing if the prefix matches nothing' do
#d.add('fiend')
#d.add('great')
#d.find('nothing').should be_empty
end
it "finds an entry" do
#d.add('fish' => 'aquatic animal')
#d.find('fish').should == {'fish' => 'aquatic animal'}
end
it 'finds multiple matches from a prefix and returns the entire entry (keyword + definition)' do
#d.add('fish' => 'aquatic animal')
#d.add('fiend' => 'wicked person')
#d.add('great' => 'remarkable')
#d.find('fi').should == {'fish' => 'aquatic animal', 'fiend' => 'wicked person'}
end
it 'lists keywords alphabetically' do
#d.add('zebra' => 'African land animal with stripes')
#d.add('fish' => 'aquatic animal')
#d.add('apple' => 'fruit')
#d.keywords.should == %w(apple fish zebra)
end
it 'can produce printable output like so: [keyword] "definition"' do
#d.add('zebra' => 'African land animal with stripes')
#d.add('fish' => 'aquatic animal')
#d.add('apple' => 'fruit')
#d.printable.should == %Q{[apple] "fruit"\n[fish] "aquatic animal"\n[zebra] "African land animal with stripes"}
end
end
and here's what I've created so far for the printable function:
class Dictionary
def initialize(opts = {})
#opts = opts
end
def entries
#opts
end
def add(opts)
opts.is_a?(String) ? #opts.merge!(opts => nil) : #opts.merge!(opts)
end
def keywords
#opts.keys.sort
end
def include?(key)
#opts.has_key?(key)
end
def find(key)
#opts.select { |word,defin| word.scan(key).join == key }
end
def printable
opts_sorted = #opts.sort_by { |word,defin| word}
opts_sorted.each do |word,defin|
print "[#{word}] \"#{defin}\"\n"
end
end
end
and here's the error:
1) Dictionary can produce printable output like so: [keyword] "definition"
Failure/Error: #d.printable.should == %Q{[apple] "fruit"\n[fish] "aquatic animal
"\n[zebra] "African land animal with stripes"}
expected: "[apple] \"fruit\"\n[fish] \"aquatic animal\"\n[zebra] \"African lan
d animal with stripes\""
got: [["apple", "fruit"], ["fish", "aquatic animal"], ["zebra", "African
land animal with stripes"]] (using ==)
Diff:
## -1,4 +1,4 ##
-[apple] "fruit"
-[fish] "aquatic animal"
-[zebra] "African land animal with stripes"
+["apple", "fruit"]
+["fish", "aquatic animal"]
+["zebra", "African land animal with stripes"]
# ./11_dictionary/dictionary_spec.rb:81:in `block (2 levels) in <top (required)>
'
Lucky for you I did this whole thing just now! Below is the answer code with comments for the explanation! I hope this still helps even after 4 years!
class Dictionary # Create the class
attr_accessor :entries # Attribute Accessor; this is our setter and getter
def initialize(entries = {}) # Create constructor; if there is no value passed the default value is {}
#entries = {} # Declare instance variable with empty hash
if entries.is_a? Hash # is_a? is a method where it sees if its a class; Hash is the class we compare it to
entries.each {|key, value| #entries[key] = value} # if there is a value that's passed we copy the hash to our instance variable
end # End conditional
end # End constructor
def keywords # Main purpose of this method is to return what's inside our keys
keywords = [] # Create empty keyword variable
#entries.each_key {|key| keywords.push(key.to_s)} # each_key method only takes the keys in our hash and pushes them into the keywords array
keywords.sort # We sort the keywords variable
end # End method
def add(entry) # add method adds in an entry either a hash or a string
if entry.is_a? Hash # If the argument belongs to the class Hash; or if its a hash
entry.each {|key, value| #entries[key] = value} # Then we copy the key and values to our instance variable
elsif entry.is_a? String # If the arguemnt belongs to the class String; or if its a String
#entries[entry] = nil # We create a key for that string and set the value to nil
end # End conditional
end # End method
def include?(entry) # include? method this checks if the argument is in our entries and returns a boolean value
#entries.has_key?(entry) # has_key? checks the instance variable if it has the key
end # End method
def find(entry) # find method finds if certain letters are in our keys
#entries.select {|key| /#{entry}/.match(key)} # select gets the keys that match a certain keyword in our entries
# if #entries.has_key?(entry) # First attepmt to solve the test case
# #entries.select {|key,value| key == entry}
# else
# puts {}
# end
end # End method
def printable # printable method just prints out our entries like a dictionary
printable = [] # Create an empty array
#entries.sort.each do |key,value| # Sort and iterate to each key-value pair
printable.push("[#{key}] \"#{value}\"") # push the key-value pair formatted accordingly to the test case
end # End hash iteration
printable.join("\n") # join with newlines to get desired result
end # End method
end # End class
I think the problem is that your printable method is not returning what you want it to return.
The return value of printable is the value of the method's last statement (opts_sorted.each ...). You are using print to output the expected string but this does not change the return value of the method (which is what you are testing).
If you want to return the string you are printing instead try this:
def printable
opts_sorted = #opts.sort_by { |word, defin| word}
opts_sorted.map{ |word, defin| "[#{word}] \"#{defin}\"\n" }.join
end
map will transform the key-value pairs in the hash into an array of strings formatted the way you want them and then join will append them into a single string.
This is the error I'm getting. I need to pass a string key to a method that usually accepts string key and string symbol combo
Dictionary
is empty when created`
can add whole entries with keyword and definition
add keywords (without definition) (FAILED - 1)
Failures:
1) Dictionary add keywords (without definition)
**Failure/Error: #d.add('fish')
NoMethodError:
undefined method `each' for "fish":String
# ./11_dictionary/dictionary.rb:9:in `add'**
# ./11_dictionary/dictionary_spec.rb:27:in `block (2 levels) in <top (required)>'
This is the following spec. the #d.add('fish') passes a string without definition.
require 'dictionary'
describe Dictionary do
before do
#d = Dictionary.new
end
it 'is empty when created' do
#d.entries.should == {}
end
it 'can add whole entries with keyword and definition' do
#d.add('fish' => 'aquatic animal')
#d.entries.should == {'fish' => 'aquatic animal'}
#d.keywords.should == ['fish']
end
it 'add keywords (without definition)' do
#d.add('fish')
#d.entries.should == {'fish' => nil}
#d.keywords.should == ['fish']
end
my code:
I think my definition.each is where it goes wrong. From what I understand each goes through the array or hash in this example but only has one value so it doesn't know how to loop through it? So I thought about setting a default nil value for meaning and word but it doesn't accept it and comes with different errors.
class Dictionary
attr_accessor :keywords, :entries
def initialize
#entries = {}
end
def add(definition)
definition.each do |word, meaning|
#entries[word] = meaning
end
end
def keywords
#entries.keys.sort
end
def include?(key)
#entries.key?(key)
end
def find(query)
#entries.select { |word, definition| word.scan(query).join == query}
end
def entries
#entries
end
end
Thanks for you help in advance!
You're trying to call each on a String in #d.add('fish'), which isn't allowed. You could just add the following line above definition.each do |word, meaning|:
definition = { definition => nil } unless definition.is_a? Hash
although this could have unexpected consequences if you are passing different kinds of objects to add.
My code is rough.. This works too.
def add(hash)
if (hash.is_a? Hash)
#d=hash
else
#d = {hash => nil}
end
So I'm trying to create a dictionary object in Ruby and get it to pass a bunch of RSPEC tests as part of a project. So far it's been good, but I'm stuck on one particular test. Here's the RSPEC up until that test:
require 'dictionary'
describe Dictionary do
before do
#d = Dictionary.new
end
it 'is empty when created' do
#d.entries.should == {}
end
it 'can add whole entries with keyword and definition' do
#d.add('fish' => 'aquatic animal')
#d.entries.should == {'fish' => 'aquatic animal'}
#d.keywords.should == ['fish']
end
it 'add keywords (without definition)' do
#d.add('fish')
#d.entries.should == {'fish' => nil}
#d.keywords.should == ['fish']
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
it "doesn't cheat when checking whether a given keyword exists" do
#d.include?('fish').should be_false # if the method is empty, this test passes with nil returned
#d.add('fish')
#d.include?('fish').should be_true # confirms that it actually checks
#d.include?('bird').should be_false # confirms not always returning true after add
end
end
Everything passes so far except for the last test "doesn't cheat when checking whether a given keyword exists". I'm trying to wrap my head around how I can get that to pass, but so far no success. Any help would be greatly appreciated. Here's what I have so far:
class Dictionary
attr_accessor :keywords, :entries
def initialize
#entries = {}
end
def add(defs)
defs.each do |word, definition|
#entries[word] = definition
end
end
def keywords
input = []
#entries.each do |key, value|
input << key
end
input.sort
end
def include?(key)
self.keywords.include?(keywords.to_s)
end
end
Thanks in advance!
There's a bug in:
self.keywords.include?(keywords.to_s)
keywords returns an array. You can't use keywords.to_s as a parameter for keywords.include? and expect it to find a match:
irb(main):002:0> keywords = %w[a b c]
=> ["a", "b", "c"]
irb(main):003:0> keywords.to_s
=> "[\"a\", \"b\", \"c\"]"
irb(main):004:0> keywords.include?(keywords.to_s)
=> false
irb(main):005:0> keywords.include?('a')
=> true
because you need to use an individual element in the keywords array if you want to find it. Notice that keywords.to_s is a String-ized version of the array, which could also be: '["a", "b", "c"]'. Hopefully that will help you recognize the problem the next time you encounter it.
From the documentation for include?:
a = [ "a", "b", "c" ]
a.include?("b") #=> true
a.include?("z") #=> false
So, change:
def include?(key)
self.keywords.include?(keywords.to_s)
end
to:
def include?(key)
self.keywords.include?(key)
end
What do you mean by "doesn't cheat"? How can code cheat? It only does what you told it. All the previous tests look like they'd rule out the conditions being tested in the "doesn't cheat" block which makes only:
#d.include?('bird').should be_false # confirms not always returning true after add
worth including in it. You could use:
#d.add('fish')
#d.include?('bird').should be_false # confirms not always returning true after add
if you really aren't sure how your code works.
Instead of building keywords using an array, which will get slower the larger your #entries list is, and results in include? running slower any time you call it,
take advantage of the fact that #entries is already a hash and use its methods:
def keywords
#entries.keys.sort
end
def include?(key)
!!#entries[key]
end
Or use this for include?:
def include?(key)
#entries.key?(key)
end
As totallymike mentions in the comment, most of the functions you want already exist in Hash. For the slightly different interfaces that you want, you should inherit Hash.
class Dictionary < Hash
def add(defs)
defs = {defs => nil} unless defs.kind_of?(Hash)
merge!(defs)
end
alias entries dup
def keywords; keys.sort end
end
Does this give you an idea how to get to pass "doesn't cheat when checking whether a given keyword exists"?
#h = Hash.new{|h,k,v| h[k] = nil}
#h["fish"]
p #h #=> {"fish"=>nil}
The {|h,k,v| h[k] = nil}part is run when a key is not present in the hash. It adds the key and gives it a nil value.
I'm having trouble figuring out this challenge. Here is what I have:
class Dictionary
attr_accessor :entries
def initialize
#x = Hash.new
end
def entries
#x
end
def add(hash)
#x.merge!(hash)
end
end
#d=Dictionary.new
#d.add('fish' => 'aquatic animal')
puts #d.entries
i'm getting => "fishaquatic animal"
I'm WANTING to get => {'fish' => 'aquatic animal'}
to_s on Hash behaves less-than-ideally for some Ruby versions. Try puts #d.entries.inspect.
Update:
The following code works for me (Ruby 1.9.3 and rspec 2.12.0):
class Dictionary
def initialize
#x = Hash.new
end
def entries
#x
end
def add(hash)
#x.merge!(hash)
end
end
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can add whole entries with keyword and definition' do
#d.add('fish' => 'aquatic animal')
#d.entries.should == {'fish' => 'aquatic animal'}
end
end
As written, your code is currently setting #x to a new, empty Hash and then returning it every time you call the entries method.
Try moving that setup code into an initialize method:
class Dictionary
attr_reader :entries
def initialize
#entries = Hash.new
end
def add(hash)
#entries.merge!(hash)
end
end
It looks like to me the rspec codes a bit weird. The second test does an entries method, and the entries method resets the instance variable #x back to blank. So it would better to add the instance variable as an attr_reader and then initialize it when you make a new dictionary object. So it would look something like this
class Dictionary
attr_reader #x
def initialize
#x = Hash.new
end
def add(hash)
#x.merge!(hash)
end
end
and the test would be like this
#d.add(fish: "aquatic animal")
#d.x.should == {'fish' => "aquatic animal"}