处理嵌套哈希以将所有值转换为字符串

时间:2016-01-04 16:06:19

标签: ruby-on-rails ruby hash

我有以下代码,它接受哈希并将所有转换为字符串。

def stringify_values obj
  @values ||= obj.clone

  obj.each do |k, v|
    if v.is_a?(Hash)
      @values[k] = stringify_values(v)
    else
      @values[k] = v.to_s
    end
  end

  return @values
end

所以给出以下哈希:

{
  post: {
    id: 123,
    text: 'foobar',
  }
}

我得到 YAML 输出

--- &1
:post: *1
:id: '123'
:text: 'foobar'

当我想要这个输出

--- 
:post: 
  :id: '123'
  :text: 'foobar'

看起来该对象已被展平,然后被赋予对自身的引用,这会导致我的规范中的堆栈级别错误。

如何获得所需的输出?

4 个答案:

答案 0 :(得分:4)

有两个问题。第一:第一次调用后的@values将始终包含您在第一次调用中克隆的对象,因此最终您将始终收到克隆的@values对象,无论您使用{ {1}}变量(这是因为您的呼叫中有obj运算符)。第二:如果你删除它并且会||= - 它仍然会返回不正确的结果(最深的哈希值),因为你在调用后覆盖了现有的变量调用。

@values = obj.clone

答案 1 :(得分:3)

stringify_values的简单实现可以是 - 假设它始终是哈希。此函数使用Active Support Core Extensions添加的Hash#deep_merge方法 - 我们将哈希与自身合并,以便在块中我们检查每个值并在其上调用to_s

def stringify_values obj
  obj.deep_merge(obj) {|_,_,v| v.to_s}
end

完整的工作样本:

require "yaml"
require "active_support/core_ext/hash"

def stringify_values obj
  obj.deep_merge(obj) {|_,_,v| v.to_s}
end

class Foo
    def to_s
        "I am Foo"
    end
end

h = {
  post: {
    id: 123,
    arr: [1,2,3],
    text: 'foobar',
    obj: { me: Foo.new}
  }
}

puts YAML.dump (stringify_values h)
#=> 
---
:post:
  :id: '123'
  :arr: "[1, 2, 3]"
  :text: foobar
  :obj:
    :me: I am Foo

当值是数组时,不确定期望是什么,因为Array#to_s也会将数组作为字符串给出,无论是否可取,您可以稍微决定并调整解决方案。

答案 2 :(得分:0)

如果你知道将值转换为字符串,我会选择monkeypatching Hash class:

class Hash
  def stringify_values
    map { |k, v| [k, Hash === v ? v.stringify_values : v.to_s] }.to_h
  end
end

现在你将能够:

require 'yaml'
{
  post: {
    id: 123,
    text: 'foobar'
  },
  arr: [1, 2, 3]
}.stringify_values.to_yaml
#⇒ ---
#  :post:
#    :id: '123'
#    :text: foobar
#  :arr: "[1, 2, 3]"

事实上,我想知道你是否真的想要争夺Array s?

答案 3 :(得分:0)

如果您想要一个简单的解决方案而不需要ActiveSupport,则可以使用each_with_object在一行中完成此操作:

obj.each_with_object({}) { |(k,v),m| m[k] = v.to_s }

如果您想就地修改obj,请传递obj作为each_with_object的参数;上面的版本返回一个新对象。