51

如何将哈希转换为 ruby​​ 中的结构?

鉴于这种:

h = { :a => 1, :b => 2 }

我想要一个这样的结构:

s.a == 1
s.b == 2
4

12 回答 12

71

如果您已经定义了一个结构,并且您想用哈希实例化一个实例:

Person = Struct.new(:first_name, :last_name, :age)

person_hash = { first_name: "Foo", last_name: "Bar", age: 29 }

person = Person.new(*person_hash.values_at(*Person.members))

=> #<struct Person first_name="Foo", last_name="Bar", age=29>
于 2014-09-30T23:22:54.130 回答
70

如果它不必特别是 aStruct而是可以是 a OpenStruct

pry(main)> require 'ostruct'
pry(main)> s = OpenStruct.new(h)
=> #<OpenStruct a=1, b=2>
pry(main)> puts s.a, s.b
1
2
于 2012-08-14T23:38:39.440 回答
54

由于 Ruby 1.9+ 中保证了哈希键顺序:

Struct.new(*h.keys).new(*h.values)
于 2012-08-14T23:59:57.743 回答
13

这是基于@elado 上面的回答,但使用keyword_init值(结构文档

你可以简单地这样做:

Person = Struct.new(:first_name, :last_name, :age, keyword_init: true)

person_hash = { first_name: "Foo", last_name: "Bar", age: 29 }

person = Person.new(person_hash)

=> #<struct Person first_name="Foo", last_name="Bar", age=29>
于 2018-08-28T13:47:08.210 回答
10

以下以可靠的方式从哈希创建结构(因为在 ruby​​ 中不保证哈希顺序):

s = Struct.new(*(k = h.keys)).new(*h.values_at(*k))
于 2012-08-14T23:16:44.167 回答
8

拥有Hash#to_struct非常实用:

class Hash
  def to_struct
    Struct.new(*keys).new(*values)
  end
end

还有一些例子:

>> { a: 1, b: 2 }.to_struct
=> #<struct a=1, b=2>
>> { a: 1, b: 2 }.to_struct.a
=> 1
>> { a: 1, b: 2 }.to_struct.b
=> 2
>> { a: 1, b: 2 }.to_struct.c
NoMethodError: undefined method `c` for #<struct a=1, b=2>

to_struct与数组一起使用的深度:

class Array
  def to_struct
    map { |value| value.respond_to?(:to_struct) ? value.to_struct : value }
  end
end

class Hash
  def to_struct
    Struct.new(*keys).new(*values.to_struct)
  end
end
于 2017-05-22T20:55:33.910 回答
1

这是一个将值映射到 Struct 正确顺序的示例:

require 'securerandom'

Message = Struct.new(:to, :from, :message, :invitee)

message_params = {from: "my@email.address", to: "your@email.address",
        invitee: SecureRandom.uuid, message: "hello"}

if Message.members.sort == message_params.keys.sort
  # Do something with the return struct object here
  Message.new *Message.members.map {|k| message_params[k] } 
else
  raise "Invalid keys for Message"
end
于 2014-09-23T17:37:19.513 回答
1

这提供了一个干净的纯只读对象,类似于 ruby​​ 结构,但具有深度转换和额外的to_h方法,可以在任何时候将结构作为哈希。

例子

foo = {a:{b:{c:123}}}.to_struct
foo.a.b.c # 123
foo.a.to_h # {b:{c:123}}

红宝石代码

class Hash
  def to_struct
    Class.new.tap do |c|
      c.define_singleton_method(:to_h) do
        m_list = methods(false) - [:to_h]
        m_list.inject({}) do |h, m|
          h[m] = send(m)
          h[m] = h[m].to_h if h[m].class == Class
          h
        end
      end

      each do |k, v|
        v = v.to_struct if v.class == Hash
        c.define_singleton_method(k) { v }
      end
    end
  end
end

不完全是问题的答案(不是 ruby​​ Struct 对象),但我在寻找答案时只需要这个,所以我将在这里发布答案。

于 2018-05-27T08:17:42.257 回答
1

如果您需要递归版本,这里有一个简洁的 hack/解决方案

a_hash = {a: {b: {c: 'x'}}}
structs_inside_structs = JSON.parse(
  a_hash.to_json, object_class: OpenStruct
)
# => #<OpenStruct a=#<OpenStruct b=#<OpenStruct c="x">>>
structs_inside_structs.a.b.c
# => "x"
于 2020-12-04T12:34:33.997 回答
0

您可以使用以下代码从 Hash 转换为 Struct:

Struct.new(*my_hash.keys.map(&:to_sym)).new(*my_hash.values)

确保将所有键转换为符号,因为它会在字符串键上出错,NameError: identifier my_key needs to be constant

我个人建议在 Hash 类中添加一个猴子补丁,因为这是一个非常强大的操作

# config/initializers/core_extensions.rb

Hash.class_eval do
  def to_struct
    Struct.new(*keys.map(&:to_sym)).new(*values)
  end
end
于 2020-12-02T22:06:51.727 回答
0

TL;博士;

使用OpenStruct.new(hash)

新的更好的方式

hash = {"Name" => "Taimoor", "id" => "222", "SomeKey" => "Some value", "attributes" => {"type" => 'User', 'role' => 'manager'}}
OpenStruct.new(hash)

这只会将第一级哈希转换为结构。要将嵌套属性哈希转换为结构,我这样做了

hash.attributes = OpenStruct.new(hash.attributes)

老路

我有一个带有字符串键的哈希

{"Name" => "Taimoor", "id" => "222", "SomeKey" => "Some value"}

所以我需要首先将键转换为符号hash.keys.map(&:to_sym)并访问原始散列中的那些键,我使用了hash.with_indifferent_access散列上的方法。

def hash_to_struct(hash)
    Struct.new(*(k = hash.keys.map(&:to_sym)))
        .new(*hash.with_indifferent_access.values_at(*k))
end

现在它将适用于哈希的符号和字符串类型键。

注意:这只会将哈希转换为一级结构。对于嵌套哈希,您需要在每个嵌套级别上调用此方法。

于 2020-12-08T07:19:59.467 回答
-1
require 'ds_hash'

data = {a: {b: 123 }}.to_struct

data.a.b == 123       # true
data.a   == {b: 123 } # true
于 2015-04-15T14:40:01.627 回答