I'm going through a problem on Ruby Monk, https://rubymonk.com/learning/books/1-ruby-primer/problems/155-restaurant#solution4804
Their solution is great; I like it and it's more compact than mine. Problem is for mine, I just don't understand why it only works when I remove the splat operator from the cost parameter orders
. Even if I shouldn't be doing it this way, I'm struggling to figure out what's up. I know sometimes it's unnecessary to understand everything, and it's best to just move on.. but curious.
Here is mine:
class Restaurant
def initialize(menu)
@menu = menu
end
def cost(*orders)
total_cost = 0
orders.each do |item, number|
total_cost += @menu[item] * number
end
end
menu = {:rice => 3, :noodles => 2}
orders = {:rice => 1, :noodles => 1}
eat = Restaurant.new(menu)
puts eat.cost(orders)
Edit: To include their suggested solution below
class Restaurant
def initialize(menu)
@menu = menu
end
def cost(*orders)
orders.inject(0) do |total_cost, order|
total_cost + order.keys.inject(0) {|cost, key| cost + @menu[key]*order[key] }
end
end
end
Edit: To clear up and answer my own question in the comment
I tried these experiments and it shows inject
"removing" the array brackets that splat "put on". Perhaps not the most proper way to think about it? It does help clear up my confusion.
order = { :rice => 1, :noodles => 1 }
menu = { :rice => 3, :noodles => 2 }
[order].inject(0) do |bla, blu|
p bla #=> 0
p blu #=> {:rice=>1, :noodles=>1}
p blu.keys #=> [:rice, :noodles]
end
When you write:
def cost(*orders)
end
then all the parameters passed to the cost
method will be put into a single array named orders
. These two are thus equivalent:
def cost(*orders)
p orders.class #=> Array
p orders #=> [1,2,3]
end
cost(1,2,3)
def cost(orders)
p orders.class #=> Array
p orders #=> [1,2,3]
end
cost( [1,2,3] ) # note the array literal brackets
In your case, when you remove the "splat" you are saying "set orders
to reference whatever was passed in directly". In this case you're passing it a Hash, and when you iterate a hash you get key/value pairs for each entry. This is just what you want.
When you do have the splat, though, you're getting this:
def cost(*orders)
p orders.class #=> Array
p orders #=> [{:rice=>1, :noodles=>1}]
end
orders = {:rice=>1, :noodles=>1}
cost(orders)
So you're wrapping your hash in an array, and then iterating over the elements of the array. Thus, the first value passed to the block is the entire hash, and there is no second parameter.
def cost(*orders)
p orders.class #=> Array
p orders #=> [{:rice=>1, :noodles=>1}]
orders.each do |item,number|
p item #=> {:rice=>1, :noodles=>1}
p number #=> nil
end
end
orders = {:rice=>1, :noodles=>1}
cost(orders)
At this point you can't multiply anything by nil
and so your code breaks.