通过传递一组键,使用values_at从Ruby哈希中提取值

时间:2014-12-30 16:07:10

标签: ruby hash

我试图通过作为参数传入的键提取哈希值:

def menu
  @menu = {"burger" => 5, "chips" => 2, "coke" => 1}
end

def receive_order(*items)
  raise "Sorry not in stock" if items.all? { |key|@menu.has_key?(key) } == false
  :ordered if items.all? { |key|@menu.has_key?(key) }
  # I was hoping to do something like this...
  bill << menu.values_at(items)
  # bill being an empty array
end

我在IRB中玩过这个,然后返回[nil]。有人能够解释原因吗?

如果我输入以下内容:

menu.values_at("burger", "chips")

我明白了:

=> [5,2]

那么为什么在作为参数传递时它不会起作用?

2 个答案:

答案 0 :(得分:7)

如果您splat the array而不是传递数组本身,

Hash#values_at将会起作用。

在发布的示例中,values_at中的方法Hash将传递的数组视为单个键,而不是将数组的各个元素视为单独的键。

尝试:

bill << menu.values_at(*items)

REPL中的示例(irb / pry):

>> hash = {a: 12, b: 7, c: 23}
=> {:a=>12, :b=>7, :c=>23}
>> keys_to_search = [:a, :b]
=> [:a, :b]
>> hash.values_at keys_to_search
=> [nil]
>> hash.values_at *keys_to_search
=> [12, 7]

答案 1 :(得分:3)

除了遗漏values_at中的splat之外,还有一些问题,以及一些改进的机会。

首先:

def menu
  @menu = {"burger" => 5, "chips" => 2, "coke" => 1}
end

在调用menu并将实例变量@menu设置为等于该哈希值时,返回哈希值都没有意义。我建议你用以下任何一个代替:

@menu = {"burger" => 5, "chips" => 2, "coke" => 1}

def menu
  {"burger" => 5, "chips" => 2, "coke" => 1}
end

并分别通过引用receive_order@menumenu方法获取哈希值。

接下来,如果商品没有库存,您为什么要提出异常?对于必须是常见情况而言,这似乎相当激烈。我希望你能以更优雅的方式处理它。另一方面,也许item的每个元素都应该是哈希中的一个键,并且该消息具有误导性。我们来看看那行代码:

raise "Sorry not in stock" if items.all? { |key| @menu.has_key?(key) } == false

这有点令人困惑。最好写:

raise "Sorry not in stock" unless items.all? { |key| @menu.has_key?(key) }

但更好的是:

raise "Sorry not in stock" if (items - @menu.keys).any?

@menu["chips"] = 0不会表示“芯片”没有库存吗?如果是这样,并且假设当item的元素不是哈希中的键时引发异常,则将上述内容更改为:

raise "No keys in @menu for elements @{items - @menu.keys} of items" \
  if (items - @menu.keys).any?
if @menu.values_at(*items).any?(&:zero?)
  puts "Sorry not in stock"
  <do something else?>
else
  ...
end

如评论中所述,在:

:ordered if items.all? { |key|@menu.has_key?(key) }

您需要使用实际执行某些操作的代码替换:ordered。但是你需要if条款吗?如果第一行没有引发异常,if子句必须评估true,所以“否”,您不需要if子句。

接下来,在执行以下操作时(在添加缺失的splat之后)会引发异常:

bill << menu.values_at(*items)
  # NameError: undefined local variable or method `bill' for main:Object

因为bill尚未初始化。我想你只想:

bill = menu.values_at(*items)

但是你不会对bill做任何事情,除了从方法中返回它的值(因为它是在方法返回之前计算的最后一个表达式的值)。因此,您可以用以下内容替换它:

menu.values_at(*items)

把它们放在一起,我们可能会有这样的事情:

@menu = {"burger" => 5, "chips" => 2, "coke" => 1}

def receive_order(*items)
  raise "No keys in @menu for elements @{items - @menu.keys} of items" \
    if (items - @menu.keys).any?
  if @menu.values_at(*items).any?(&:zero?)
    puts "Sorry not in stock"
    <do something else?>
  else
    <perform some action when everything is in stock>
  end
  @menu.values_at(*items)
end