Find product's frequency in this CashRegister class - ruby

I have 3 simple classes CashRegister, Bill and Position. A CashRegister is composed of Bill objects and a Bill object is composed of Position objects. They're implemented as followed
class CashRegister
def initialize
#bills = []
end
def product_frequency
#???
end
def << bill
#bills << bill
self
end
end
class Bill
attr_reader :positions,:nr
protected :positions
def initialize(nr)
#nr = nr
#positions = []
end
def << pos
#positions << pos
self
end
end
class Position
attr_reader :product,:quantity,:price
def initialize(product,quantity,single_price)
#product = product
#quantity = quantity
#price = single_price * quantity
end
end
I want to write a product_frequency method that calculates how often a product is bought in the CashRegister. This method returns a hash as a result, with the product as key, and the frequency as value.
An example would be:
pos1 = Position.new('Chicken', 5, 12)
pos2 = Position.new('Soup', 6, 24)
pos3 = Position.new('Burger', 3, 19)
pos4 = Position.new('Chicken', 2, 12)
pos5 = Position.new('Soup', 8, 24)
pos6 = Position.new('Burger', 9, 19)
bill1 = Bill.new(1) << pos1 << pos2 << pos3 #Chicken: 5;Soup: 6;Burger: 3
bill2 = Bill.new(2) << pos4 << pos3 << pos2 #Chicken: 2;Soup: 6;Burger: 3
bill3 = Bill.new(3) << pos6 << pos6 << pos6 #Chicken: 0;Soup: 0;Burger: 27
bill4 = Bill.new(4) << pos4 << pos5 << pos4 #Chicken: 4;Soup: 8;Burger: 0
my_cash_register = CashRegister.new << bill1 << bill2 << bill3 << bill4
my_cash_register.product_frequency #{'Chicken' => 11, 'Soup' => 20, 'Burger' => 33}
How can I accomplish that result?

Taken mostly from the solution of Bartosz Bonisławski. But since positionen in bill is protected, we first have to make it accessible by defining an each function in Bill that takes in a block and applies it to every position of positions. We also do the same for CashRegister.
class CashRegister
def initialize
#bills = []
end
def each(&block)
#bills.each(&block)
end
def product_frequency
result = {}
each { |bill|
bill.each { |position|
result[position.product] ||= 0
result[position.product] += position.quantity
}
}
result
end
def << bill
#bills << bill
self
end
end
class Bill
attr_reader :positions,:nr
protected :positions
def initialize(nr)
#nr = nr
#positions = []
end
def each(&block)
#positions.each(&block)
end
def << pos
#positions << pos
self
end
end
class Position
attr_reader :product,:quantity,:price
def initialize(product,quantity,single_price)
#product = product
#quantity = quantity
#price = single_price * quantity
end
end

If you just want to count how many times each product was bought, then here it is:
def product_frequency
product_frequency = {}
#bills.each do |bill|
bill.positionen.each do |position|
#product_frequency[position.product] ||= 0
#product_frequency[position.product] += position.quantity
end
end
product_frequency
end
It's just one way of doing it. Code is pretty easy, so I think you can figure how it works

You could define #frequency for each nested class and then recursively collect hashes.
class Hash
def additive_merge!(hash)
hash.each { |k,v| self[k] ? self[k] += v : self[k] = v }
self
end
end
class CashRegister
def initialize
#bills = []
end
def product_frequency
#bills.map(&:frequency).reduce(&:additive_merge!)
end
def << bill
#bills << bill
self
end
end
class Bill
attr_reader :positionen,:nr
protected :positionen
def initialize(nr)
#nr = nr
#positions = []
end
def << pos
#positions << pos
self
end
def frequency
#positions.map(&:frequency).reduce(&:additive_merge!)
end
end
class Position
attr_reader :product,:quantity,:price
def initialize(product,quantity,single_price)
#product = product
#quantity = quantity
#price = single_price * quantity
end
def frequency
{ product => quantity }
end
end

Related

Undefined function make_card Ruby

I am doing a practice problem black jack card game in ruby, references I am using are
https://medium.com/quick-code/using-ruby-classes-to-implement-a-game-of-blackjack-535a786c417
I am getting error that says undefined method "make_card"
Code for my Deck Class
class Deck
def initialize
#faces = [*(2..10),'Jack','Queen','King','Ace']
#suits = ['clubs','spades','hearts','diamonds']
#cards = []
#faces.each do |face|
if face.class == "Integer"
value = face
elsif face == 'Ace'
value = 11
else
value = 10
end
#suits.each do |suit|
#cards << Card.new(face,suit,value)
end
end
#cards.shuffle!
end
def make_card(participant)
fresh_card = Card.new(face,suit,value)
participant.turn << fresh_card
participant.total = participant.total + fresh_card.value
end
def deal(number,participant)
number.times{#cards.shift.make_card(participant)}
end
end
As both methods are in same class I am still getting that error
Solved it by placing method in Card class and accessing it via its instance variable
class Card
attr_accessor :face, :suit, :value
def initialize(face,suit,value)
#face = face
#suit = suit
#value = value
end
def make_card(participant)
fresh_card = Card.new(face,suit,value)
participant.turn << fresh_card
participant.total = participant.total + fresh_card.value
end
end
class Deck
def initialize
#faces = [*(2..10),'Jack','Queen','King','Ace']
#suits = ['clubs','spades','hearts','diamonds']
#cards = []
#faces.each do |face|
if face.class == Integer
value = face
elsif face == 'Ace'
value = 11
else
value = 10
end
#suits.each do |suit|
#cards << Card.new(face,suit,value)
end
end
#cards.shuffle!
end
def deal(number,participant)
number.times{#cards.shift.make_card(participant)}
end
end

Deck of cards that deals a number of cards to a number of players

I built a deck of cards:
class Card
attr_accessor :rank, :suit
def initialize(rank, suit)
#rank = rank
#suit = suit
end
def output_card
puts "#{self.rank} of #{self.suit}"
end
end
class Deck
def initialize
#cards = []
#ranks = ["Ace", 2, 3, 4, 5, 6, 7, 8, 9, 10, "Jack", "Queen", "King"]
#suits = [:hearts, :spades, :diamonds, :clubs]
#suits.each do |suit|
#ranks.each do |rank|
#cards << Card.new(rank, suit)
end
end
end
def shuffle
#cards.shuffle!
end
def deal
#cards.shift
end
def output_deck
#cards.each do |card|
card.output_card
end
end
end
deck = Deck.new
deck.shuffle
deck.deal
deck.output_deck
puts "Show top card:"
deck.deal.output_card
I am struggling to add Dealer class to deal a specified number of cards to a specified number of players.
If anyone would explain that, I would greatly appreciate it.
class Player
attr_accessor :name, :cards
def initialize(name)
#name = name
#cards = []
end
end
class HoldEmPoker
def cards_per_player
2
end
end
class Dealer
def initialize(deck, game)
#deck = deck
#game = game
end
def deal_to(player)
#game.cards_per_player.times do
player.cards << #deck.deal
end
end
end
p1 = Player.new("Negreanu")
p2 = Player.new("Ivey")
p3 = Player.new("Hellmuth")
players = [p1, p2, p3]
deck = Deck.new
deck.shuffle
game = HoldEmPoker.new
dealer = Dealer.new(deck, game)
players.each { |p| dealer.deal_to(p) }
p1.cards #=> an array with two Card objects
If you want, you can add a deal method to Dealer class
def deal(players)
players.each do |player|
#game.cards_per_player.times do
player.cards << #deck.deal
end
end
end
players = [...]
dealer.deal(players)

How to count votes, and deal with undefined method 'push' for nil:NilClass

My goal is to make a voting machine that counts candidates' votes into totals. Eventually I want to add the option to write-in a candidate, but I'm stuck with this error:
undefined method 'push' for nil:NilClass) at line 30.
I defined castVote, why isn't it being recognized?
class Candidate
attr_accessor :name
attr_accessor :count, :total
def initialize(cname)
#name = cname
#vote = Array.new
end
def totalVotes
sum = 0
if #count > 0
#count.each { |c| sum += c }
#total = sum
else
#total = 0
end
end
def castVote(vote)
if vote.is_a?(Integer) || vote.is_a?(Float)
#count.push(vote)
totalVotes
end
end
#Candidate
candidate1 = Candidate.new("Donald Duck")
#Next line is where error occurs
candidate1.castVote(1)
candidate1.castVote(1)
candidate2 = Candidate.new("Minnie Mouse")
candidate2.castVote(1)
candidate3 = Candidate.new("Goofy")
finalResults = Array.new
finalResults[0] = candidate1
finalResults[1] = candidate2
finalResults[2] = candidate3
finalResults.each { |candidate| puts "Name: " + candidate.name + "Score " + candidate.totalVotes }
end
You left out the #count instance variable in your initialize method so it's nil everywhere in that class and never gets initialized:
class Candidate
attr_accessor :name
attr_accessor :count, :total
def initialize(cname)
#name = cname
#vote = Array.new
#count = []
end
def totalVotes
sum = 0
if #count.length > 0
#count.each { |c| sum += c }
#total = sum
else
#total = 0
end
end
def castVote(vote)
if vote.is_a?(Integer) || vote.is_a?(Float)
#count.push(vote)
totalVotes
end
end
#Candidate
candidate1 = Candidate.new("Donald Duck")
#Next line is where error occurs
candidate1.castVote(1)
candidate1.castVote(1)
candidate2 = Candidate.new("Minnie Mouse")
candidate2.castVote(1)
candidate3 = Candidate.new("Goofy")
finalResults = Array.new
finalResults[0] = candidate1
finalResults[1] = candidate2
finalResults[2] = candidate3
finalResults.each { |candidate| puts "Name: " + candidate.name + "Score " + candidate.totalVotes }
end

Object that can be coerced to either String, Fixnum, or Float?

Is there a way to define an object that can be coerced to either a String or a Fixnum or a Float? This is for use in a system that collects values and evaluates a restricted set of simple expressions with them.
I tried:
class EmptyValue < Numeric
def to_s; ''; end
def to_str; ''; end
def to_i; 0; end
def to_int; 0; end
def to_f; 0.0; end
end
but this fails for
1 + e
TypeError: EmptyValue can't be coerced into Fixnum
A little more poking and this worked for all my use cases:
class EmptyValue < Numeric
def to_s; ''; end
def to_str; ''; end
def to_i; 0; end
def to_int; 0; end
def to_f; 0.0; end
def +(other)
case other
when String
to_s + other
when Fixnum
to_i + other
when Float
to_f + other
end
end
end
This is what I've done long time ago:
class NullObject
attr_reader :null_object_type, :recorded_messages
alias ρ recorded_messages
def initialize( type_of_null_object = nil )
#null_object_type = type_of_null_object
#recorded_messages = []
end
def null_object? null_type = nil
null_object_type == null_type
end
alias null? null_object?
def to_a; [] end
def to_s; "null #{null_object_type}".strip end
def to_f; 0.0 end
def to_i; 0 end
def present?; false end
def empty?; true end
def blank?; true end
def inspect
"NullObject #{null_object_type}".strip
end
def method_missing ß, *aj, &b # :nodoc:
#recorded_messages << [ ß, aj, b ]; self
end
def respond_to? ß, *aj, &b # :nodoc:
true
end
protected
def == other # :nodoc:
null_object_type == other.null_object_type
end
end # class NullObject
# Strong zero.
#
ZERO = NullObject.new
ZERO.instance_exec {
ɪ = self
singleton_class.class_exec do
define_method :zero do ɪ end
end
def * other; other.class.zero end
def / other
self unless other.zero?
raise ZeroDivisionError, "The divisor is zero! (#{other})"
end
def + other; other end
def - other; -other end
def coerce other
return other, other.class.zero
end
def zero?; true end
def to_s; "∅" end
def inspect; to_s end
def to_f; 0.0 end
def to_i; 0 end
def == other
z = begin
other.class.zero
rescue NoMethodError
return false
end
other == z
end
}
class << Numeric; def zero; 0.0 end end
class << Integer; def zero; 0 end end
class << Float; def zero; 0.0 end end
class << Rational; def zero; Rational 0, 1 end end
class << Complex; def zero; Complex 0, 0 end end
class << String; def zero; '' end end
class << Array; def zero; [] end end
class << Hash; def zero; {} end end
So, now you have 0 + ZERO #=> 0. I called this "strong zero". But it was a hack. I have a gut feeling, that this is not such a good practice.

can instances of a super class communicate with each other just as instances of a normal class

Instances of a class can access methods of other instances, but is it possible to do this for two distinct subclasses of a class with different methods? Or must the methods accessed be included in the super class?
It sounds like you need to keep track of your associations by passing in the Deck instance to the individual Hand instances. Here is an example based on the scenario you described:
class Card
attr_accessor :suit, :value
def initialize(suit, value)
#suit = suit
#value = value
end
end
class Deck
SUITS = ["Spades", "Clubs", "Diamonds", "Hearts"]
VALUES = [2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K", "A"]
HAND_SIZE = 5
attr_accessor :cards
def initialize
shuffle
end
def shuffle
#cards = []
SUITS.each do |suit|
VALUES.each do |value|
#cards << Card.new(suit, value)
end
end
#cards.shuffle!
end
def deal_hand
shuffle if #cards.size < (HAND_SIZE + 1)
hand = #cards[0...HAND_SIZE]
#cards = #cards.drop(HAND_SIZE)
hand
end
end
class Player
attr_accessor :deck
attr_accessor :hand
def initialize(deck)
#deck = deck
end
def draw!
#hand = #deck.deal_hand
end
end
# initialize
the_deck = Deck.new
players = []
players << (player1 = Player.new(the_deck))
players << (player2 = Player.new(the_deck))
players << (player3 = Player.new(the_deck))
players << (player4 = Player.new(the_deck))
# draw
players.each do |player|
player.draw!
end
# play
# ...
Hope this helps.

Resources