Решение на Трета задача от Камен Китанов

Обратно към всички решения

Към профила на Камен Китанов

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 19 успешни тест(а)
  • 0 неуспешни тест(а)

Код

require 'bigdecimal'
require 'bigdecimal/util' # добавя String#to_d
class Cart
def initialize(items, copons)
@cart_items = Hash.new(0)
@cart_coupons = {}
@items = items
@copons = copons
@out = Output.new
end
def add(item_name, count = 1)
if ( @items[item_name] == nil or count <= 0 or count > 99 )
raise "Invalid parameters passed."
end
@cart_items[item_name] += count
end
def use(coupon_name)
if ( @copons[coupon_name] == nil )
raise "Invalid parameters passed."
end
@cart_coupons[coupon_name] = @copons[coupon_name]
end
def total
# Compute all coupon discounts and sum it at the end
# Also take care of the possibility for negative total
total = self.total_without_coupons
discounts = [0] + @cart_coupons.map { |name,cop| process_coupon(name, total, total)[1] }
return [total + discounts.inject(:+),0].max()
end
def invoice
invoice = @out.write_line + @out.write("Name", "qty", "price") + @out.write_line
invoice += invoice_items + invoice_coupons
invoice += @out.write_line + @out.write( "TOTAL", "", total.to_s ) + @out.write_line
return invoice
end
def total_without_coupons
# Merge items and promotions into "sums" array and sum it at the end
sums = [0] + @cart_items.map { |name,count| count*@items[name].price.to_d() }
sums += @cart_items.map do |name,count|
@items[name].promotion.discount(@items[name].price.to_d(),count)
end
return sums.inject(:+)
end
def invoice_items
invoice = ""
@cart_items.each do |name,count|
promotion = @items[name].promotion
price = @items[name].price.to_d()
invoice += @out.write( name, count.to_s(), (count*price).to_s() )
invoice += @out.write( promotion.description(), "", promotion.discount(price,count))
end
return invoice
end
def invoice_coupons
invoice = ""
limit = total_without_coupons
@cart_coupons.each do |name,details|
coupon_info = process_coupon(name, total_without_coupons, limit )
invoice += @out.write( coupon_info[0], "", coupon_info[1] )
limit -= coupon_info[1]
end
return invoice
end
# Returns an array containing the coupon description and the discount amount
# In case of an amount coupon we do not go over the limit
def process_coupon( coupon_name, price, limit )
coupon = @copons[coupon_name]
discount = coupon.values.first
if ( coupon.keys.first == :percent )
return ["Coupon #{coupon_name} - #{discount}% off",-price*discount/100.0]
end
if ( coupon.keys.first == :amount )
amount = [discount.to_d,limit].min()
return ["Coupon #{coupon_name} - #{'%0.2f' % discount} off",-amount ]
end
end
end
class Inventory
def initialize()
@items = {}
@coupons = {}
@carts = []
end
def register(item_name, item_price, promotion = {} )
if ( item_name.length > 40 or item_price.to_d < 0.01 or item_price.to_d > 999.99 )
raise "Invalid parameters passed."
end
if @items[item_name] != nil
raise "Invalid parameters passed."
end
@items[item_name] = Item.new( item_price, Promotion.new(promotion) )
end
def register_coupon(coupon_name, coupon)
@coupons[coupon_name] = coupon
end
def new_cart
cart = Cart.new(@items,@coupons)
@carts.push(cart)
return cart
end
end
class Item
def initialize(price, promotion)
@price = price
@promotion = promotion
end
attr_accessor :price,:promotion
end
class Promotion
def initialize(promotion)
@name = promotion.keys.first
@details = promotion.values.first
end
# Calculate dicount based on the type of promotion
def discount(item_price, item_count)
if ( name == :get_one_free ) then return -(item_count / details)*item_price
elsif ( name == :package )
packages = (item_count / details.keys.first)
return -packages*details.keys.first*(details.values.first/100.0)*item_price
elsif ( name == :threshold )
packages = [item_count - details.keys.first,0].max
return -packages*(details.values.first/100.0)*item_price
end
return 0
end
# Get description based on the type of promotion
def description
if ( name == :get_one_free )
return " (buy #{details-1}, get 1 free)"
elsif ( name == :package )
return " (get #{details.values.first}% off for every #{details.keys.first})"
elsif ( name == :threshold )
ordinal = self.ordinal(details.keys.first)
return " (#{details.values.first}% off of every after the #{ordinal})"
end
return ""
end
# Add ordinal suffix to the number and return it
def ordinal(number)
# See http://en.wikipedia.org/wiki/English_numerals
table = ["th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"]
number_as_string = number.to_s()
units = number_as_string[number_as_string.length-1].to_i()
tens = 0
if ( number_as_string.length >= 2 )
tens = number_as_string[number_as_string.length-2] .to_i()
end
if ( tens == 1) then return number_as_string+"th"
else return number_as_string+table[units] end
end
attr_accessor :name,:details
end
class Output
def write(name, count, price )
# Items with empty promotions will try to write to the output empty lines
if ( name == "" ) then return "" end
# Write Name and Quantity columns
number_of_whitespaces = 48 - name.length - 2 - count.length
invoice = "| #{name}" + " " * number_of_whitespaces + count + " |"
# Price could be a String - we need to catch exceptions
begin
price_string = "%0.2f" % price
rescue
price_string = price
end
# Write Price column
number_of_whitespaces = 10 - price_string.length - 1
invoice += " "*number_of_whitespaces + price_string + " |\n"
return invoice
end
def write_line
return "+------------------------------------------------+----------+\n"
end
end

Лог от изпълнението

...................

Finished in 0.60497 seconds
19 examples, 0 failures

История (1 версия и 0 коментара)

Камен обнови решението на 05.11.2011 22:16 (преди около 13 години)

+require 'bigdecimal'
+require 'bigdecimal/util' # добавя String#to_d
+
+class Cart
+ def initialize(items, copons)
+ @cart_items = Hash.new(0)
+ @cart_coupons = {}
+ @items = items
+ @copons = copons
+ @out = Output.new
+ end
+
+ def add(item_name, count = 1)
+ if ( @items[item_name] == nil or count <= 0 or count > 99 )
+ raise "Invalid parameters passed."
+ end
+
+ @cart_items[item_name] += count
+ end
+
+ def use(coupon_name)
+ if ( @copons[coupon_name] == nil )
+ raise "Invalid parameters passed."
+ end
+
+ @cart_coupons[coupon_name] = @copons[coupon_name]
+ end
+
+ def total
+ # Compute all coupon discounts and sum it at the end
+ # Also take care of the possibility for negative total
+ total = self.total_without_coupons
+ discounts = [0] + @cart_coupons.map { |name,cop| process_coupon(name, total, total)[1] }
+ return [total + discounts.inject(:+),0].max()
+ end
+
+ def invoice
+ invoice = @out.write_line + @out.write("Name", "qty", "price") + @out.write_line
+ invoice += invoice_items + invoice_coupons
+ invoice += @out.write_line + @out.write( "TOTAL", "", total.to_s ) + @out.write_line
+ return invoice
+ end
+
+ def total_without_coupons
+ # Merge items and promotions into "sums" array and sum it at the end
+ sums = [0] + @cart_items.map { |name,count| count*@items[name].price.to_d() }
+ sums += @cart_items.map do |name,count|
+ @items[name].promotion.discount(@items[name].price.to_d(),count)
+ end
+ return sums.inject(:+)
+ end
+
+ def invoice_items
+ invoice = ""
+ @cart_items.each do |name,count|
+ promotion = @items[name].promotion
+ price = @items[name].price.to_d()
+ invoice += @out.write( name, count.to_s(), (count*price).to_s() )
+ invoice += @out.write( promotion.description(), "", promotion.discount(price,count))
+ end
+ return invoice
+ end
+
+ def invoice_coupons
+ invoice = ""
+ limit = total_without_coupons
+ @cart_coupons.each do |name,details|
+ coupon_info = process_coupon(name, total_without_coupons, limit )
+ invoice += @out.write( coupon_info[0], "", coupon_info[1] )
+ limit -= coupon_info[1]
+ end
+ return invoice
+ end
+
+ # Returns an array containing the coupon description and the discount amount
+ # In case of an amount coupon we do not go over the limit
+ def process_coupon( coupon_name, price, limit )
+ coupon = @copons[coupon_name]
+ discount = coupon.values.first
+
+ if ( coupon.keys.first == :percent )
+ return ["Coupon #{coupon_name} - #{discount}% off",-price*discount/100.0]
+ end
+ if ( coupon.keys.first == :amount )
+ amount = [discount.to_d,limit].min()
+ return ["Coupon #{coupon_name} - #{'%0.2f' % discount} off",-amount ]
+ end
+ end
+
+end
+
+class Inventory
+ def initialize()
+ @items = {}
+ @coupons = {}
+ @carts = []
+ end
+
+ def register(item_name, item_price, promotion = {} )
+ if ( item_name.length > 40 or item_price.to_d < 0.01 or item_price.to_d > 999.99 )
+ raise "Invalid parameters passed."
+ end
+
+ if @items[item_name] != nil
+ raise "Invalid parameters passed."
+ end
+
+ @items[item_name] = Item.new( item_price, Promotion.new(promotion) )
+ end
+
+ def register_coupon(coupon_name, coupon)
+ @coupons[coupon_name] = coupon
+ end
+
+ def new_cart
+ cart = Cart.new(@items,@coupons)
+ @carts.push(cart)
+ return cart
+ end
+
+end
+
+class Item
+ def initialize(price, promotion)
+ @price = price
+ @promotion = promotion
+ end
+
+ attr_accessor :price,:promotion
+end
+
+class Promotion
+ def initialize(promotion)
+ @name = promotion.keys.first
+ @details = promotion.values.first
+ end
+
+ # Calculate dicount based on the type of promotion
+ def discount(item_price, item_count)
+ if ( name == :get_one_free ) then return -(item_count / details)*item_price
+ elsif ( name == :package )
+ packages = (item_count / details.keys.first)
+ return -packages*details.keys.first*(details.values.first/100.0)*item_price
+ elsif ( name == :threshold )
+ packages = [item_count - details.keys.first,0].max
+ return -packages*(details.values.first/100.0)*item_price
+ end
+
+ return 0
+ end
+
+ # Get description based on the type of promotion
+ def description
+ if ( name == :get_one_free )
+ return " (buy #{details-1}, get 1 free)"
+ elsif ( name == :package )
+ return " (get #{details.values.first}% off for every #{details.keys.first})"
+ elsif ( name == :threshold )
+ ordinal = self.ordinal(details.keys.first)
+ return " (#{details.values.first}% off of every after the #{ordinal})"
+ end
+
+ return ""
+ end
+
+ # Add ordinal suffix to the number and return it
+ def ordinal(number)
+ # See http://en.wikipedia.org/wiki/English_numerals
+ table = ["th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"]
+
+ number_as_string = number.to_s()
+ units = number_as_string[number_as_string.length-1].to_i()
+ tens = 0
+ if ( number_as_string.length >= 2 )
+ tens = number_as_string[number_as_string.length-2] .to_i()
+ end
+
+ if ( tens == 1) then return number_as_string+"th"
+ else return number_as_string+table[units] end
+
+ end
+
+ attr_accessor :name,:details
+end
+
+class Output
+ def write(name, count, price )
+ # Items with empty promotions will try to write to the output empty lines
+ if ( name == "" ) then return "" end
+
+ # Write Name and Quantity columns
+ number_of_whitespaces = 48 - name.length - 2 - count.length
+ invoice = "| #{name}" + " " * number_of_whitespaces + count + " |"
+
+ # Price could be a String - we need to catch exceptions
+ begin
+ price_string = "%0.2f" % price
+ rescue
+ price_string = price
+ end
+
+ # Write Price column
+ number_of_whitespaces = 10 - price_string.length - 1
+ invoice += " "*number_of_whitespaces + price_string + " |\n"
+
+ return invoice
+ end
+
+ def write_line
+ return "+------------------------------------------------+----------+\n"
+ end
+
+end