Unfortunately, I get the following error. I can't quite understand why it doesn't work?
:14:in `convert': undefined method `factors' for 30:Fixnum (NoMethodError)
from question_stack.rb:18:in `<main>'
I try to create the following class:
# Class Wordgame
class Wordgame
WORDGAME_MAP = {
'3' => 'baa',
'5' => 'bar',
'7' => 'bla'
}.freeze
def self.factors
(1..self).select { |n| (self % n).zero? }
end
def self.convert(number)
number.factors.map(&:to_s).each.map { |char| WORDGAME_MAP[char] }.join
end
end
Wordgame.convert(30)
What am I doing wrong? Where is my mental error?
self refers to the class itself in a class method or to the current object in an instance method. In your case it refers to WordGame, the object's class.
If you really want it to refer to 30 into the factors method you have to define it as an instance method, because called on an object (30), not a class (Integer), opening the Integer class
class Integer
def factors
(1..self).select { |n| (self % n).zero? }
end
end
I think you know the alternative:
def self.factors(x)
(1..x).select { |n| (self % n).zero? }
end
def self.convert(number)
factors(number).map(&:to_s).each.map { |char| WORDGAME_MAP[char] }.join
end
Or better, with OOP.
class WordGame
def initialize(n)
#n = n
end
def convert
factors.map(&:to_s).each.map { |char| WORDGAME_MAP[char] }.join
end
private
def factors
(1..#n).select { |m| (#n % m).zero? }
end
end
Wordgame.new(30).convert
Related
I'm attempting to adapt the method-chaining example cited in this posting (Method chaining and lazy evaluation in Ruby) to work with an object that implements the Enumerable class (Implement a custom Enumerable collection class in Ruby )
Coffee class:
class Coffee
attr_accessor :name
attr_accessor :strength
def initialize(name, strength)
#name = name
#strength = strength
end
def <=>(other_coffee)
self.strength <=> other_coffee.strength
end
def to_s
"<name: #{name}, strength: #{strength}>"
end
end
Criteria class:
class Criteria
def initialize(klass)
#klass = klass
end
def criteria
#criteria ||= {:conditions => {}}
end
# only show coffee w/ this strength
def strength(strength)
criteria[:strength] = strength
self
end
# if there are multiple coffees, choose the first n=limit
def limit(limit)
criteria[:limit] = limit
self
end
# allow collection enumeration
def each(&block)
#klass.collection.select { |c| c[:strength] == criteria[:strength] }.each(&block)
end
end
CoffeeShop class:
class CoffeeShop
include Enumerable
def self.collection
#collection=[]
#collection << Coffee.new("Laos", 10)
#collection << Coffee.new("Angkor", 7)
#collection << Coffee.new("Nescafe", 1)
end
def self.limit(*args)
Criteria.new(self).limit(*args)
end
def self.strength(*args)
Criteria.new(self).strength(*args)
end
end
When I run this code:
CoffeeShop.strength(10).each { |c| puts c.inspect }
I get an error:
criteria.rb:32:in block in each': undefined method '[]' for #<Coffee:0x007fd25c8ec520 #name="Laos", #strength=10>
I'm certain that I haven't defined the Criteria.each method correctly, but I'm not sure how to correct it. How do I correct this?
Moreover, the each method doesn't support the limit as currently written. Is there a better way to filter the array such that it is easier to support both the strength and limit?
Other coding suggestions are appreciated.
Your Coffee class defines method accessors for name and strength. For a single coffee object, you can thus get the attributes with
coffee.name
# => "Laos"
coffee.strength
# => 10
In your Criteria#each method, you try to access the attributes using the subscript operator, i.e. c[:strength] (with c being an Instance of Coffee in this case). Now, on your Coffee class, you have not implemented the subscript accessor which resulting in the NoMethodError you see there.
You could thus either adapt your Criteria#each method as follows:
def each(&block)
#klass.collection.select { |c| c.strength == criteria[:strength] }.each(&block)
end
or you could implement the subscript operators on your Coffee class:
class Coffee
attr_accessor :name
attr_accessor :strength
# ...
def [](key)
public_send(key)
end
def []=(key, value)
public_send(:"#{key}=", value)
end
end
Noe, as an addendum, you might want to extend your each method in any case. A common (and often implicitly expected) pattern is that methods like each return an Enumerator if no block was given. This allows patterns like CoffeeShop.strength(10).each.group_by(&:strength).
You can implement this b a simple on-liner in your method:
def each(&block)
return enum_for(__method__) unless block_given?
#klass.collection.select { |c| c.strength == criteria[:strength] }.each(&block)
end
I am new to ruby and would love some help please :)
I fixed the error in my ruby code, but I'm confused as to WHY the fix works.
I get the following error private method `select' called for nil:NilClass (NoMethodError) when I type in the following line:
scanned = #keywords.select { |key| key.match(word) }
, which is located within the following method:
def find(word)
return {} unless #entries.any?
scanned = #keywords.select { |key| key.match(word) }
found ={}
if scanned.any?
scanned.each do |x| found.merge!( { x => #entries[x] } )
end
else
{}
end
found
end
,yet this error goes away if I replace the erroneous line above with the following:
scanned = #entries.keys.sort.select { |key| key.match(word) }
I define #keywords as #keywords = #entries.keys.sort in the class structure below... Why can't I use '.select' on #keywords yet I can use it on #keywords' contents?
class Dictionary
def initialize
#entries = {}
end
def entries
#entries
end
def add(entry)
#entries = entry.is_a?(Hash) ? #entries.merge!(entry) : #entries.merge!( {entry => nil} )
end
def keywords
#keywords = #entries.keys.sort
end
def include?(keyword)
#entries.keys.include?(keyword)
end
def find(word)
return {} unless #entries.any?
scanned = #keywords.select { |key| key.match(word) }
found ={}
if scanned.any?
scanned.each do |x| found.merge!( { x => #entries[x] } )
end
else
{}
end
found
end
def printable
printable = #entries.sort.map do |key, value| %Q{[#{key}] "#{value}"}
end
printable.join("\n")
end
end
You are never calling the method keywords, and thus, you are never executing the code that populates #keywords with data. It remains nil.
#keywords will remain nil until you call the method keywords, which will initialize #keywords to be the sorted entry keys.
It looks like you're calling find on a dictionary object without first calling keywords. This would result in a nil value for #keywords.
I am getting an error when running this code. The Following is the output:
L
Bicycle#Ex3.rb:32:in `spares': private method `select' called for nil:NilClass (NoMethodError)
from Bicycle#Ex3.rb:10:in `spares'
from Bicycle#Ex3.rb:111:in `<main>'
Here is the code:
class Bicycle
attr_reader :size, :parts
def initialize(args={})
#size = args[:size]
#parts = args[:parts]
end
def spares
parts.spares # return an array
end
def lead_days
1
end
#...
end
class Parts
attr_reader :parts
def initialize(args={})
#parts = parts
end
def size
parts.size
end
def spares
parts.select{|part| part.needs_spare}
end
end
class Part
attr_reader :name, :description, :needs_spare
def initialize(args)
#name = args[:name]
#description = args[:description]
#needs_spare = args.fetch(:needs_spare, true)
end
end
class RoadBikeParts < Parts
attr_reader :tape_color
def post_initialize(args)
#tape_color = args[:tape_color]
end
def local_spares
{tape_color: tape_color}
end
def default_tire_size
'23'
end
end
class MountainBikeParts < Parts
attr_reader :front_shock, :rear_shock
def post_initialize(args)
#front_shock = args[:front_shock]
#rear_shock = args[:rear_shock]
end
def local_spares
{ rear_shock: rear_shock}
end
def default_tire_size
'2.1'
end
end
chain = Part.new(
name: 'chain',
description: '10 speed')
road_tire = Part.new(
name: 'tape_size',
description: '23')
tape = Part.new(
name: 'tape_color',
description: 'red')
mountain_tire = Part.new(
name: 'tire_size',
description: '2.1')
rear_shock = Part.new(
name: 'rear_shock',
description: 'Fox')
front_shock = Part.new(
name: 'front_shock',
description: 'Manitou',
needs_spare: false)
road_bike_part = Parts.new([chain, road_tire, tape])
road_bike = Bicycle.new(
size: 'L',
parts: Parts.new([chain,
road_tire,
tape]))
puts road_bike.size
#puts road_bike.parts.size
puts road_bike.spares.size
It is clear this line --> puts road_bike.spares.size is given the error NoMethodError, however, I am not sure how I can make a work around to correct this issue for this example. The spares method is returning an array of Part objects, however it seems my problem lies in the fact the spares method .select is private from the calling object.
Any advice to revise this code would be great. Thanks.
What's happening here is that Parts#parts is nil. You're getting the error on this line:
# parts is nil
parts.select{|part| part.needs_spare}
In the initializer of Parts, its parts attribute does not get assigned properly:
def initialize(args={})
#parts = parts
end
So when being initialized, it assigns #parts with the value of parts. But since parts is not a local variable there, it calls the Parts#parts method, which returns nil.
If you change the initializer to the following:
def initialize(parts)
#parts = parts
end
You'll be able to run the code. But subclasses of Parts seem to expect a Hash in the initializer, rather than an Array like their super class does though.
I can't inherit the Struct. I must implement class which act like Struct.
Is there a way improve my code for use "ClassName" and functional like Struct ? And write k=Dave.new("Rachel" , "Greene") ???
class MyStruct
def self.new(*attributes)
puts "ppp"
dynamic_name = "ClassName"
Kernel.const_set(dynamic_name,Class.new() do
attributes.each do |action|
self.send(:define_method, action) {
puts "call #{action}"
}
end
end
)
end
end
# class ClassName
# def new *args
# puts "iii"
# end
# end
Dave = MyStruct.new(:name, :surname)
k=Dave.new() # k=Dave.new("Rachel" , "Greene")
k.surname
k.name
Here is a version of your code which works:
class MyStruct
def self.new(*attributes)
Class.new do
self.send(:attr_accessor, *attributes)
self.send(:define_method, :initialize) do |*values|
values.each_with_index { |val, i| self.send("#{attributes[i]}=", val) }
end
end
end
end
Dave = MyStruct.new(:name, :surname)
k = Dave.new('Rachel', 'Green')
# => #<Dave:0x00000001af2b10 #name="Rachel", #surname="Green">
k.name
# => "Rachel"
k.surname
# => "Green"
You don't need to const_set inside the method - Dave = is enough
I'm creating here an attr_accessor for each of the attributes, so you are getting a getter and a setter for each
In the initialize method I'm sending each value to its corresponding setter, to set all values. If there are less values than anticipated, the last attributes will not be set, if there are more - an exception will be thrown (undefined method '=')
Have you looked at the Struct class in Ruby?
http://www.ruby-doc.org/core-2.1.2/Struct.html
class MyStruct < Struct.new(:first_name, :last_name)
end
MyClassObj = MyStruct.new("Gavin", "Morrice")
Also, you shouldn't ever overwrite self.new, define initialize instead
For academic reasons, I'd like to make an instance of Ruby class act like a hash.
GOALS
Initialize MyClass instance with a hash # success
Request values from instance of myClass, like a hash # success
Then set properties as a hash # fail
Although some discussion exists, I tried what's out there (1, 2) with no success. Let me know what I'm doing wrong. Thanks!
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
my_hash[key]
end
def set_prop(key, value)
myhash[key] = value
end
end
test = myClass.new({:a => 3}) #=> #<MyClass:0x007f96ca943898 #my_hash={:a=>3}>
test[:a] #=> 3
test[:b] = 4 #=> NameError: undefined local variable or method `myhash' for #<MyClass:0x007f96ca9d0ef0 #my_hash={:a=>3}>
You declared set_prop, but you're using []= in tests. Did you mean to get this?
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
my_hash[key]
end
def []=(key, value)
my_hash[key] = value
end
end
test = MyClass.new({:a => 3}) # success
test[:a] # success
test[:b] = 4 # success
test.my_hash # => {:a=>3, :b=>4}
module HashizeModel
def [](key)
sym_key = to_sym_key(key)
self.instance_variable_get(sym_key)
end
def []=(key, value)
sym_key = to_sym_key(key)
self.instance_variable_set(sym_key, value)
end
private
def to_sym_key(key)
if key.is_a? Symbol
return ('#'+key.to_s).to_sym
else
return ('#'+key.to_s.delete('#')).to_sym
end
end
end
You should write it as test = MyClass.new({:a => 3}) and the below code should work.
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
#my_hash[key]
end
def []=(key,val)
#my_hash[key]=val
end
def set_prop(key, value)
#myhash[key] = value
end
end
test = MyClass.new({:a => 3})
test[:a]
test[:b]= 4
test.my_hash # => {:a=>3, :b=>4}