Home > Back-end >  Ruby - Hash function with unknown number of arguments
Ruby - Hash function with unknown number of arguments

Time:01-06

I'm trying to make a calorie counter for the below hash menu. in this example I've passed 3 arguments - what would the function need to look like it the number of parameters/arguments is unknown?

@menu = {
  "hamburger" => 250,
  "Cheese burger" => 350, 
  "cola" => 35, 
  "salad" => 120,
  "dessert" => 350
}
  
def order(a, b, c)
  return @menu[a]   @menu[b]   @menu[c]
end

puts order("hamburger", "Cheese burger", "cola")
tried 
def order(**a)
  total = 0
  total  = @menu[**a]
end

i know (*a) works for arrays.

I'd like to be able to get results for puts order("hamburger") and equally for puts order("Cheese burger", "salad"), for example

CodePudding user response:

In Ruby, it is often possible to write the code exactly the same way you would describe the solution in English. In this case, you need to get the values at specific keys of the hash and then compute the sum of the values.

You can use the Hash#values_at method to get the values at the specific keys and you can use the Array#sum method to compute the sum of the values:

def order(*items)
  @menu.values_at(*items).sum
end

Note that it is strange to use an instance variable of the top-level main object. It would make much more sense to use a constant:

MENU = {
  'hamburger'     => 250,
  'Cheese burger' => 350, 
  'cola'          => 35, 
  'salad'         => 120,
  'dessert'       => 350,
}

def order(*items)
  MENU.values_at(*items).sum
end

It would also make sense to freeze the hash:

MENU = {
  'hamburger'     => 250,
  'Cheese burger' => 350, 
  'cola'          => 35, 
  'salad'         => 120,
  'dessert'       => 350,
}.freeze

And last but not least, I find the name of the order method somewhat misleading. It is also ambiguous: is order meant to be a noun and this is meant to be a getter method that retrieves an order? Or is it meant to be a verb and it is meant to be a command method which tells the object to execute an order?

Either way, it does not seem that the method is doing either of those two things, rather it seems to compute a total. So, the name should probably reflect that.

CodePudding user response:

I would do:

MENU = {
  "hamburger" => 250,
  "Cheese burger" => 350, 
  "cola" => 35, 
  "salad" => 120,
  "dessert" => 350
}

def order(*args)
  MENU.values_at(*args).sum
end

order("hamburger", "Cheese burger", "cola")
#=> 635

Read about the Ruby Splat Operator, Hash#values_at and Array#sum.


When you really want to use each (what I would not recommend), like mentioned in the comment, then you can implement it like this:

def order(*args)
  total = 0
  args.each { |name| total  = MENU[name] }
  total
end

or

def order(*args)
  total = 0
  MENU.values_at(*args).each { |value| total  = value }
  total
end
  • Related