Ruby-on-rails 如何优雅地对“嵌套”散列进行符号化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24927653/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to elegantly symbolize_keys for a 'nested' hash
提问by SHS
Consider the following code:
考虑以下代码:
hash1 = {"one" => 1, "two" => 2, "three" => 3}
hash2 = hash1.reduce({}){ |h, (k,v)| h.merge(k => hash1) }
hash3 = hash2.reduce({}){ |h, (k,v)| h.merge(k => hash2) }
hash4 = hash3.reduce({}){ |h, (k,v)| h.merge(k => hash3) }
hash4 is a 'nested' hash i.e. a hash with string keys and similarly 'nested' hash values.
hash4 是一个“嵌套”散列,即带有字符串键和类似“嵌套”散列值的散列。
The 'symbolize_keys' method for Hash in Rails lets us easily convert the string keys to symbols. But I'm looking for an elegantway to convert all keys (primary keys plus keys of all hashes within hash4) to symbols.
Rails 中 Hash 的 'symbolize_keys' 方法让我们可以轻松地将字符串键转换为符号。但我正在寻找一种优雅的方式将所有键(主键加上 hash4 中所有散列的键)转换为符号。
The point is to save myself from my (imo) ugly solution:
关键是要从我的(imo)丑陋的解决方案中拯救自己:
class Hash
def symbolize_keys_and_hash_values
symbolize_keys.reduce({}) do |h, (k,v)|
new_val = v.is_a?(Hash) ? v.symbolize_keys_and_hash_values : v
h.merge({k => new_val})
end
end
end
hash4.symbolize_keys_and_hash_values #=> desired result
FYI: Setup is Rails 3.2.17 and Ruby 2.1.1
仅供参考:安装程序是 Rails 3.2.17 和 Ruby 2.1.1
Update:
更新:
Answer is hash4.deep_symbolize_keysfor Rails <= 5.0
答案是hash4.deep_symbolize_keysRails <= 5.0
Answer is JSON.parse(JSON[hash4], symbolize_names: true)for Rails > 5
答案是JSON.parse(JSON[hash4], symbolize_names: true)Rails > 5
回答by jvnill
There are a few ways to do this
有几种方法可以做到这一点
There's a
deep_symbolize_keysmethod in Railshash.deep_symbolize_keys!As mentioned by @chrisgeeq, there is a
deep_transform_keysmethod that's available from Rails 4.hash.deep_transform_keys(&:to_sym)There is also a bang
!version to replace the existing object.There is another method called
with_indifferent_access. This allows you to access a hash with either a string or a symbol like howparamsare in the controller. This method doesn't have a bang counterpart.hash = hash.with_indifferent_accessThe last one is using
JSON.parse. I personally don't like this because you're doing 2 transformations - hash to json then json to hash.JSON.parse(JSON[h], symbolize_names: true)
Rails 中有一个
deep_symbolize_keys方法hash.deep_symbolize_keys!正如@chrisgeeq 所提到的,
deep_transform_keysRails 4 提供了一种方法。hash.deep_transform_keys(&:to_sym)还有一个 bang
!版本来替换现有的对象。还有一种方法叫做
with_indifferent_access. 这允许您使用字符串或符号访问哈希,就像params在控制器中一样。这种方法没有 bang 对应物。hash = hash.with_indifferent_access最后一个是使用
JSON.parse. 我个人不喜欢这个,因为你正在做 2 个转换 - 散列到 json 然后 json 到散列。JSON.parse(JSON[h], symbolize_names: true)
UPDATE:
更新:
16/01/19 - add more options and note deprecation of deep_symbolize_keys
2019 年 1 月 16 日 - 添加更多选项并注意弃用 deep_symbolize_keys
19/04/12 - remove deprecated note. only the implementation used in the method is deprecated, not the method itself.
2012 年 4 月 19 日 - 删除已弃用的注释。只有方法中使用的实现被弃用,而不是方法本身。
回答by Mikhail Chuprynski
You cannot use this method for params or any other instance of ActionController::Parametersany more, because deep_symbolize_keysmethod is deprecated in Rails 5.0+ due to security reasons and will be removed in Rails 5.1+ as ActionController::Parametersno longer
inherits from Hash
您不能将此方法用于 params 或任何其他实例ActionController::Parameters,因为deep_symbolize_keys出于安全原因,该方法在 Rails 5.0+ 中已弃用,并将在 Rails 5.1+ 中删除,因为ActionController::Parameters不再继承自 Hash
So this approach by @Uri Agassi seems to be the universalone.
所以@Uri Agassi 的这种方法似乎是通用的。
JSON.parse(JSON[h], symbolize_names: true)
However, Rails Hash object still does have it.
然而,Rails Hash 对象仍然拥有它。
So options are:
所以选项是:
if you don't use Rails or just don't care:
JSON.parse(JSON[h], symbolize_names: true)with Rails and ActionController::Parameters:
params.to_unsafe_h.deep_symbolize_keyswith Rails and plain Hash
h.deep_symbolize_keys
如果您不使用 Rails 或只是不在乎:
JSON.parse(JSON[h], symbolize_names: true)使用 Rails 和 ActionController::Parameters:
params.to_unsafe_h.deep_symbolize_keys使用 Rails 和普通哈希
h.deep_symbolize_keys
回答by Alexander
In rails you can create HashWithIndifferentAccess class. Create an instance of this class passing your hash to its constructor and then access it with keys that are symbols or strings (like params of Controller's Actions):
在 Rails 中,您可以创建 HashWithIndifferentAccess 类。创建此类的实例,将哈希传递给其构造函数,然后使用符号或字符串的键(如控制器操作的参数)访问它:
hash = {'a' => {'b' => [{c: 3}]}}
hash = hash.with_indifferent_access
# equal to:
# hash = ActiveSupport::HashWithIndifferentAccess.new(hash)
hash[:a][:b][0][:c]
=> 3
回答by Uri Agassi
I can suggest something like this:
我可以提出这样的建议:
class Object
def deep_symbolize_keys
self
end
end
class Hash
def deep_symbolize_keys
symbolize_keys.tap { |h| h.each { |k, v| h[k] = v.deep_symbolize_keys } }
end
end
{'a'=>1, 'b'=>{'c'=>{'d'=>'d'}, e:'f'}, 'g'=>1.0, 'h'=>nil}.deep_symbolize_keys
# => {:a=>1, :b=>{:c=>{:d=>"d"}, :e=>"f"}, :g=>1.0, :h=>nil}
You can also easily extend it to support Arrays:
您还可以轻松扩展它以支持Arrays:
class Array
def deep_symbolize_keys
map(&:deep_symbolize_keys)
end
end
{'a'=>1, 'b'=>[{'c'=>{'d'=>'d'}}, {e:'f'}]}.deep_symbolize_keys
# => {:a=>1, :b=>[{:c=>{:d=>"d"}}, {:e=>"f"}]}
回答by mirage
Might I suggest:
我可以建议:
JSON.parse(hash_value.to_json)
回答by Cary Swoveland
You could use:
你可以使用:
- Hash#to_sto convert the hash to a string;
- String#gsubwith a regex to convert the keys from strings to representations of symbols; and then
- Kernel#evalto convert the string back into a hash.
- Hash#to_s将哈希值转换为字符串;
- String#gsub使用正则表达式将键从字符串转换为符号表示;进而
- Kernel#eval将字符串转换回散列。
This is an easy solution to your problem, but, you should only consider using it if you can trust that evalis not going to produce something nasty. If you have control over the content of the hash being converted, that should not be a problem.
这是您问题的简单解决方案,但是,只有在您相信eval不会产生令人讨厌的东西时才应该考虑使用它。如果您可以控制要转换的散列的内容,那应该不是问题。
This approach could be used for other kinds of nested objects, such as ones containing both arrays and hashes.
这种方法可以用于其他类型的嵌套对象,例如包含数组和散列的对象。
Code
代码
def symbolize_hash(h)
eval(h.to_s.gsub(/\"(\w+)\"(?==>)/, ':'))
end
Examples
例子
symbolize_hash(hash4)
#=> {:one=>{:one=> {:one=> {:one=>1, :two=>2, :three=>3},
# :two=> {:one=>1, :two=>2, :three=>3},
# :three=>{:one=>1, :two=>2, :three=>3}},
# :two=> {:one=> {:one=>1, :two=>2, :three=>3},
# :two=> {:one=>1, :two=>2, :three=>3},
# :three=>{:one=>1, :two=>2, :three=>3}},
# :three=>{:one=> {:one=>1, :two=>2, :three=>3},
# :two=> {:one=>1, :two=>2, :three=>3},
# :three=>{:one=>1, :two=>2, :three=>3}}},
# :two=>{:one=> {:one=> {:one=>1, :two=>2, :three=>3},
# ...
# :three=>{:one=>{:one=> {:one=>1, :two=>2, :three=>3},
# ...
# :three=>{:one=>1, :two=>2, :three=>3}}}}
symbolize_hash({'a'=>1, 'b'=>[{'c'=>{'d'=>'d'}}, {e:'f'}]})
#=> {:a=>1, :b=>[{:c=>{:d=>"d"}}, {:e=>"f"}]}
Explanation
解释
(?==>)in the regex is a zero-width postive lookahead. ?=signifies positive lookahead; =>is the string that must immediately follow the match to \"(\w+)\". \1in ':\1'(or I could have written ":\\1") is a string beginning with a colon followed by a backreference to the content of capture group #1, the key matching \w+(without the quotes).
(?==>)在正则表达式中是一个零宽度正前瞻。?=表示正向前瞻;=>是必须紧跟在匹配之后的字符串\"(\w+)\"。\1in ':\1'(或者我可以写":\\1")是一个以冒号开头的字符串,后跟对捕获组 #1 内容的反向引用,键匹配\w+(不带引号)。

