如何将哈希的键访问为对象属性

3
假设我有一个名为w_data的哈希表。
 {"latitude"=>"40.695", "air_temperature"=>"-10", "longitude"=>"-96.854", "datetime"=>"2014-01-01 02:55:00"}

我希望通过 w_data.latitude 访问它的值,而不是 w_data["latitude"]。如何实现?

如果只涉及“纬度”,则可以在“Hash”类上简单地定义一个同名方法:class Hash; def latitude; self["latitude"]; end; end。然后,w_data.latitude #=> "40.695"。如果您想对所有键执行此操作,请循环使用键并使用“define_method”。 - Cary Swoveland
我不确定这是一个好主意。如果你这样做,你必须确保没有键的名称(在 to_s 之后)与哈希响应的方法的名称相同(keysvaluesfirst 等)。 - Cary Swoveland
4个回答

9
我要说的是不要使用OpenStruct,因为它会每次创建新的对象都清空方法缓存

相反,考虑使用像hashie-mash这样的gem,或者自己编写类似哈希的代码:

Hashie::Mash

hsh = Hashie::Mash.new("latitude"=>"40.695", "air_temperature"=>"-10", "longitude"=>"-96.854", "datetime"=>"2014-01-01 02:55:00")
hsh.latitude
 => "40.695"

自定义解决方案:

class AccessorHash < Hash
  def method_missing(method, *args)
    s_method = method.to_s
    if s_method.match(/=$/) && args.length == 1
      self[s_method.chomp("=")] = args[0]
    elsif args.empty? && key?(s_method)
      self.fetch(s_method)
    elsif args.empty? && key?(method)
      self.fetch(method)
    else
      super
    end
  end
end

hsh = AccessorHash.new("latitude"=>"40.695", "air_temperature"=>"-10", "longitude"=>"-96.854", "datetime"=>"2014-01-01 02:55:00")
hsh.latitude # => "40.695"
hsh.air_temperature = "16"
hsh => # {"latitude"=>"40.695", "air_temperature"=>"16", "longitude"=>"-96.854", "datetime"=>"2014-01-01 02:55:00"}

我会说这是我要推荐的宝石。+1 - Surya

6
如果你想要一个纯 Ruby 解决方案,只需打开 Hash 类并升级 method_missing 方法即可!
class Hash
  def method_missing method_name, *args, &block
    return self[method_name] if has_key?(method_name)
    return self[$1.to_sym] = args[0] if method_name.to_s =~ /^(.*)=$/

    super
  end
end

现在,每个哈希都有这种能力。
hash = {:five => 5, :ten => 10}
hash[:five]  #=> 5
hash.five  #=> 5

hash.fifteen = 15
hash[:fifteen]  #=> 15
hash.fifteen  #=> 15

method_missing 是 Ruby 中的一个特性,可以捕获尝试调用不存在(尚未存在)的方法。我已将此转化为一篇博客文章,并提供了交互式 Codewars kata。详情请参见:

http://www.rubycuts.com/kata-javascript-object


1
这是一个不错的建议。但是,在字符串键的情况下,它不起作用。但是,您可以通过添加另一行来解决这个问题:return self[method_name.to_s] if has_key?(method_name.to_s) :-) - Surya
是的,你可以在这里扩展它,但我想提供一个易于理解的解决方案。再加上几行 Ruby 代码会使它更加健壮 :) - Jaime Bellmyer

2

将哈希转换为OpenStruct。这里:

require 'ostruct'
w_data = OpenStruct.new
hash = {"latitude"=>"40.695", "air_temperature"=>"-10", "longitude"=>"-96.854", "datetime"=>"2014-01-01 02:55:00"}
w_data.marshal_load(hash)
w_data.longitude
#=> "-96.854"

另一种更简单的方法:
require 'ostruct'
hash = {"latitude"=>"40.695", "air_temperature"=>"-10", "longitude"=>"-96.854", "datetime"=>"2014-01-01 02:55:00"}
w_data = OpenStruct.new(hash)
w_data.longitude
#=> "-96.854"

0
如果您正在使用Ruby on Rails,可以利用ActiveSupport :: OrderedOptions模块将哈希键作为属性访问。该模块扩展了常规哈希的功能,允许您使用点符号访问其键。
此外,您可以使用ActiveSupport :: InheritableOptions通过向其传递现有哈希来创建新对象。所以您可以放心使用。
例如:
h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' })
h.girl # => 'Mary'
h.boy  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接