ruby-on-rails 将嵌套的HashWithIndifferentAccess转换为嵌套的哈希

hts6caw3  于 2022-11-19  发布在  Ruby
关注(0)|答案(1)|浏览(163)

有没有一种好的方法可以将HashWithIndifferentAccess的示例(包含HashWithIndifferentAccess类的嵌套示例)转换为Hash的示例(包含Hash类的嵌套示例)?
将嵌套的Hash转换为嵌套的HashWithIndifferentAccess似乎很容易。只需使用ActiveSupport提供的with_indifferent_access方法。这将转换所有哈希值,无论嵌套有多深。

hash = { late: { package: 2, trial: 100, penalty: { amount: 1 } },
         no_show: { package: 1, trial: 100, penalty: { amount: 2 } } }
hash_wid = hash.with_indifferent_access
hash_wid.class
# ActiveSupport::HashWithIndifferentAccess #great
hash_wid [:no_show][:penalty].class
# ActiveSupport::HashWithIndifferentAccess #great

反过来看似乎不那么容易:

hash = hash_wid.to_h
hash.class
# Hash # OK
hash[:no_show][:penalty].class
# ActiveSupport::HashWithIndifferentAccess # want this to be Hash

Hash#to_h方法只转换顶层哈希,不转换嵌套哈希。
我尝试了扩展Hash类的(Rails/ActiveSupport)deep_transform_values!方法:

hash_wid.deep_transform_values! do |value|
  value.class == HashWithIndifferentAccess ? value.to_h : value
end
hash_wid.class
# ActiveSupport::HashWithIndifferentAccess # want this to be Hash
hash_wid[:no_show][:penalty].class
# ActiveSupport::HashWithIndifferentAccess # want this to be Hash

但是看看deep_transform_values!方法的源代码(以及它所依赖的transform_values!方法),这些方法可以转换Hash类的散列,但不能转换HashWithIndifferentAccess类的散列。
那么,有没有一种很好的方法可以将嵌套的HashWithIndifferentAccess转换为嵌套的Hash呢?
谢谢
丹尼尔

cfh9epnr

cfh9epnr1#

这个例子表明你使用的键和值的类型与JSON兼容。如果你的哈希值可以转换成JSON,那么一个简单的方法就是:

hash = JSON.load(JSON.dump({ foo: { bar: 'baz' }.with_indifferent_access }.with_indifferent_access))
=> {"foo"=>{"bar"=>"baz"}}

hash.class
=> Hash

hash['foo'].class
=> Hash

deep_transform_values在您情况下不起作用,这是因为该方法的编写方式:

def _deep_transform_values_in_object(object, &block)
      case object
      when Hash
        object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
      when Array
        object.map { |e| _deep_transform_values_in_object(e, &block) }
      else
        yield(object)
      end
    end

如果你的对象是一个Hash(或Array),那么这个方法会递归地调用它自己,直到它找到一个非Hash和非Array的值,它可以在这个值上应用转换。不过,用这个例子来编写你自己的实现应该是很简单的:

def _deep_transform_values_in_object(object, &block)
      case object
      when Hash
        # something less ugly than but similar to this
        object = object.to_h if object.is_a?(ActiveSupport::HashWithIndifferentAccess)
        object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
      when Array
        object.map { |e| _deep_transform_values_in_object(e, &block) }
      else
        yield(object)
      end
    end

相关问题