我有以下代码,它接受哈希并将所有值转换为字符串。
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'
看起来该对象已被展平,然后被赋予对自身的引用,这会导致我的规范中的堆栈级别错误。
如何获得所需的输出?
答案 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
的参数;上面的版本返回一个新对象。