Ruby 将哈希中的值转换为局部变量

问题描述 投票:0回答:4

假设我有这个哈希:

entry = {"director"=>"Chris Nolan", "producer"=>"Sum Duk", "writer"=>"Saad Bakk"}

我想将每个键提取到其自己的局部变量中并具有关联的值:

director = "Chris Nolan"
producer = "Sum Duk"
...

通过使用循环而不是:

director = entry["director"]

因为有很多值,我不想单独做它们。

我发现它几乎完美地工作,除了它创建了一个实例变量并且我想要一个局部变量,但由于某种原因

local_variable_set
不存在。

entry.each_pair { |k, v| instance_variable_set("@#{k}", v) }

有解决办法吗?或者如果失败的话,有一种方法可以将实例变量转换为本地变量并删除实例而不需要逐一执行吗?

ruby
4个回答
12
投票

选项1

除了好玩之外,我不能推荐这个,但它主要具有你想要的效果:

entry.each |k, v|
  singleton_class.send(:attr_accessor, k)
  send("#{k}=", v)
end

director                        # => "Chris Nolan"
self.director = "Wes Anderson"  # Unfortunately, must use self for assignment
director                        # => "Wes Anderson"

它不是创建局部变量,而是在当前对象的单例类上定义访问方法,您可以像调用局部变量一样调用它们。

为了使它们更加“本地化”,您可以在使用完这些方法后使用

singleton_class.remove_method
删除它们。 您甚至可以尝试为任何现有的单例类方法添加相同名称的别名,然后再恢复它们。

选项2

这是我在实际代码中使用的东西。它需要 Ruby On Rails 附带的 ActiveSupport gem,但也可以单独使用。

director, producer, writer = entry.values_at('director', 'producer', 'writer')

不幸的是,它需要输入每个变量名称两次,而不是您要求的零次。如果您确定哈希中值的顺序,您可以编写:

director, producer, writer = entry.values  # Not so good, IMO.

我对这个版本感到不安,因为现在创建哈希的代码有一个不明显的责任来确保哈希按一定顺序排列。

注意

如果您的目标只是减少输入,这里有一种方法,每次变量访问只需要比真正的局部变量多两个字符:

e = OpenStruct.new(entry)
e.director     # => "Chris Nolan"

4
投票

由于变量作用域,您无法创建局部变量。
如果您在块内创建局部变量,则该变量只能在块本身内部访问。
请参阅此问题以获取更多信息。
在 Ruby 中动态设置局部变量


4
投票

您可以使用

eval
执行此操作,但对这些变量的所有操作都必须在它们定义的范围内。

例如,这将起作用:

vals = {
  "foo" => "bar",
  "baz" => "qux"
}

eval <<-EOF
  #{ vals.map {|k, v| "#{k} = \"#{v}\""}.join("\n") }
  puts foo
EOF

但是,这不会:

vals = {
  "foo" => "bar",
  "baz" => "qux"
}

eval <<-EOF
  #{ vals.map {|k, v| "#{k} = \"#{v}\""}.join("\n") }
EOF

puts foo

as

foo
在评估结束时超出范围。但是,如果您对变量的所有工作都可以在 eval 范围内完成,那么这是完全可行的。


0
投票

扩展@antinome的注释:如果您希望局部变量存在,因为您想通过在字符串中插入它们来动态访问它们,那么

OpenStruct
+
instance_eval
提供了这种强大的方式:

# entry = {"director"=>"Chris Nolan", "producer"=>"Sum Duk", "writer"=>"Saad Bakk"}
require 'ostruct'
o = OpenStruct.new(entry)
o.instance_eval("Directed by #{director}, produced by #{producer}")
o.instance_eval {
  puts "Directed by #{director}"
  puts "Produced by #{producer}"
}
© www.soinside.com 2019 - 2024. All rights reserved.