在Ruby中将哈希中的所有键从字符串转换为符号的(最快/最干净/直接)方式是什么?
解析YAML时,这将很方便。
my_hash = YAML.load_file('yml')
我希望能够使用:
my_hash[:key]
而不是:
my_hash['key']
hash.symbolize_keys
hash.deep_symbolize_keys
如果您使用的是Rails ,请执行此操作。
在Ruby中将哈希中的所有键从字符串转换为符号的(最快/最干净/直接)方式是什么?
解析YAML时,这将很方便。
my_hash = YAML.load_file('yml')
我希望能够使用:
my_hash[:key]
而不是:
my_hash['key']
hash.symbolize_keys
hash.deep_symbolize_keys
如果您使用的是Rails ,请执行此操作。
Answers:
在Ruby> = 2.5(docs)中,您可以使用:
my_hash.transform_keys(&:to_sym)
使用旧的Ruby版本?这是一种单行代码,它将散列复制到带有符号键的新散列中:
my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
使用Rails可以使用:
my_hash.symbolize_keys
my_hash.deep_symbolize_keys
each_with_object
:my_hash.each_with_object({}){|(k,v), h| h[k.to_sym] = v}
.tap
方法消除最后通过的需要memo
。我创建了所有解决方案(以及递归解决方案)的清理版本gist.github.com/Integralist/9503099
如果您使用的是Rails,这是一种更好的方法:
结束。
如果不是,只需撕掉他们的代码(它也在链接中):
myhash.keys.each do |key|
myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end
symbolize_keys
新的且有效的(Rails 3)URL。我最初只是为修复了URL to_options
,但是该链接的文档为零。symbolize_keys
实际上有一个描述,所以我改用它。
hash.stringify_keys
可以使用。
对于Ruby中YAML的特定情况,如果键以' :
' 开头,则它们将被自动插入为符号。
需要“ yaml” 需要'pp' yaml_str =“ 连接: -主机:host1.example.com 港口:10000 -主机:host2.example.com 端口:20000 ” yaml_sym =“ :连接: -:host:host1.example.com :端口:10000 -:host:host2.example.com :端口:20000 ” pp yaml_str = YAML.load(yaml_str) 放置yaml_str.keys.first.class pp yaml_sym = YAML.load(yaml_sym) 放置yaml_sym.keys.first.class
输出:
#/opt/ruby-1.8.6-p287/bin/ruby〜/ test.rb {“连接数” => [{“ port” => 10000,“ host” =>“ host1.example.com”}, {“端口” => 20000,“主机” =>“ host2.example.com”}]} 串 {:connections => [{:port => 10000,:host =>“ host1.example.com”}, {:port => 20000,:host =>“ host2.example.com”}]} 符号
YAML#load_file
所有键默认设置为符号,而不是将每个键都不必以冒号开头的字符串?
如果您使用的是Rails,则更加简单-您可以使用HashWithIndifferentAccess并以String和Symbols形式访问键:
my_hash.with_indifferent_access
也可以看看:
http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html
或者,您可以使用很棒的“ Ruby Facets” Gem,它包含了Ruby Core和Standard Library类的许多扩展。
require 'facets'
> {'some' => 'thing', 'foo' => 'bar'}.symbolize_keys
=> {:some=>"thing", :foo=>"bar}
另请参见:http : //rubyworks.github.io/rubyfaux/?doc=http : //rubyworks.github.io/facets/docs/facets-2.9.3/core.json#api-class-Hash
由于Ruby 2.5.0
可以使用Hash#transform_keys
或Hash#transform_keys!
。
{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}
stringify_keys
或symbolize_keys
。
http://api.rubyonrails.org/classes/Hash.html#method-i-symbolize_keys
hash = { 'name' => 'Rob', 'age' => '28' }
hash.symbolize_keys
# => { name: "Rob", age: "28" }
这是一种深度象征对象的方法
def symbolize(obj)
return obj.inject({}){|memo,(k,v)| memo[k.to_sym] = symbolize(v); memo} if obj.is_a? Hash
return obj.inject([]){|memo,v | memo << symbolize(v); memo} if obj.is_a? Array
return obj
end
我真的很喜欢Mash宝石。
你可以做mash['key']
,或者mash[:key]
,或者mash.key
如果您使用的是json,并且想将其用作哈希,则可以在Ruby核心中进行:
json_obj = JSON.parse(json_str, symbolize_names: true)
symbolize_names:如果设置为true,则返回JSON对象中名称(键)的符号。否则返回字符串。字符串是默认值。
symbol_hash = JSON.parse(JSON.generate(YAML.safe_load(FILENAME)), symbolize_names: true)
这是一种非常干燥(但效率低下)的方法,可以从YAML文件中快速获取带有嵌套键作为符号的哈希。
对@igorsales答案的修改
class Object
def deep_symbolize_keys
return self.inject({}){|memo,(k,v)| memo[k.to_sym] = v.deep_symbolize_keys; memo} if self.is_a? Hash
return self.inject([]){|memo,v | memo << v.deep_symbolize_keys; memo} if self.is_a? Array
return self
end
end
在Rails中,您可以使用:
{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!
转换为:
{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
deep_symbolize_keys
是在Rails的Hash扩展中添加的,但它不是Ruby核心的一部分。
您可能会很懒,将其包装在lambda
:
my_hash = YAML.load_file('yml')
my_lamb = lambda { |key| my_hash[key.to_s] }
my_lamb[:a] == my_hash['a'] #=> true
但这仅适用于从哈希读取-而不是写入。
为此,您可以使用 Hash#merge
my_hash = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(YAML.load_file('yml'))
init块将按需一次转换键,但是如果在访问符号版本后更新键的字符串版本的值,则不会更新符号版本。
irb> x = { 'a' => 1, 'b' => 2 }
#=> {"a"=>1, "b"=>2}
irb> y = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(x)
#=> {"a"=>1, "b"=>2}
irb> y[:a] # the key :a doesn't exist for y, so the init block is called
#=> 1
irb> y
#=> {"a"=>1, :a=>1, "b"=>2}
irb> y[:a] # the key :a now exists for y, so the init block is isn't called
#=> 1
irb> y['a'] = 3
#=> 3
irb> y
#=> {"a"=>3, :a=>1, "b"=>2}
您还可以使init块不更新哈希,这样可以保护您免受此类错误的影响,但是您仍然容易受到相反的影响-更新符号版本不会更新字符串版本:
irb> q = { 'c' => 4, 'd' => 5 }
#=> {"c"=>4, "d"=>5}
irb> r = Hash.new { |h,k| h[k.to_s] }.merge(q)
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called
#=> 4
irb> r
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called again, since this key still isn't in r
#=> 4
irb> r[:c] = 7
#=> 7
irb> r
#=> {:c=>7, "c"=>4, "d"=>5}
因此,要注意的是在这两种键形式之间切换。坚持一个。
这个怎么样:
my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))
# my_hash['key'] => "val"
# my_hash[:key] => "val"
这是针对使用mruby
但symbolize_keys
未定义任何方法的人的:
class Hash
def symbolize_keys!
self.keys.each do |k|
if self[k].is_a? Hash
self[k].symbolize_keys!
end
if k.is_a? String
raise RuntimeError, "Symbolizing key '#{k}' means overwrite some data (key :#{k} exists)" if self[k.to_sym]
self[k.to_sym] = self[k]
self.delete(k)
end
end
return self
end
end
方法:
String
RuntimeError
!
的symbolize_keys
。否则工作正常。
字符串= [“ HTML”,“ CSS”,“ JavaScript”,“ Python”,“ Ruby”]
符号= []
strings.each {| x | symbol.push(x.intern)}
因此,这可能是在Ruby中将字符串转换为数组中的符号的最直接的方法。创建一个字符串数组,然后创建一个新变量,并将该变量设置为一个空数组。然后在使用“ .each”方法创建的第一个数组中选择每个元素。然后,使用块代码“压入”新数组中的所有元素,并使用“ .intern或.to_sym”将所有元素转换为符号。
符号之所以更快,是因为它们可以节省代码中的更多内存,并且您只能使用一次。符号最常用于哈希中的键,这很棒。我不是最好的ruby程序员,但是这种形式的代码对我有很大帮助。如果有人知道更好的方法,请分享,您也可以使用此方法进行哈希!
如果您想要香草红宝石解决方案,而由于我没有访问权限,ActiveSupport
这里是深层象征解决方案(与以前的解决方案非常相似)
def deep_convert(element)
return element.collect { |e| deep_convert(e) } if element.is_a?(Array)
return element.inject({}) { |sh,(k,v)| sh[k.to_sym] = deep_convert(v); sh } if element.is_a?(Hash)
element
end
从Psych 3.0 开始,您可以添加symbolize_names:选项
Psych.load("---\n foo: bar")
# => {"foo"=>"bar"}
Psych.load("---\n foo: bar", symbolize_names: true)
# => {:foo=>"bar"}
注意:如果您的Psych版本低于3.0,symbolize_names:
将被忽略。
我的Ubuntu 18.04随同ruby 2.5.1p57一起提供
ruby-1.9.2-p180 :001 > h = {'aaa' => 1, 'bbb' => 2}
=> {"aaa"=>1, "bbb"=>2}
ruby-1.9.2-p180 :002 > Hash[h.map{|a| [a.first.to_sym, a.last]}]
=> {:aaa=>1, :bbb=>2}
a
方括号括起来以分解block参数,以使其更加简洁。例如,请参阅我的答案。
这不完全是一种单行代码,但是它会将所有字符串键转换为符号,也将嵌套的符号转换为符号:
def recursive_symbolize_keys(my_hash)
case my_hash
when Hash
Hash[
my_hash.map do |key, value|
[ key.respond_to?(:to_sym) ? key.to_sym : key, recursive_symbolize_keys(value) ]
end
]
when Enumerable
my_hash.map { |value| recursive_symbolize_keys(value) }
else
my_hash
end
end
Facets的Hash#deep_rekey也是一个不错的选择,尤其是:
样品:
require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey
在ruby中,我发现这是将哈希中的字符串键转换为符号的最简单易懂的方法:
my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)}
对于哈希中的每个键,我们在其上调用delete,以将其从哈希中删除(删除也返回与已删除键相关联的值),我们立即将其设置为符号键。
与先前的解决方案相似,但写法有所不同。
代码不会使传入的哈希值发生变化。
module HashUtils
def symbolize_keys(hash)
transformer_function = ->(key) { key.to_sym }
transform_keys(hash, transformer_function)
end
def stringify_keys(hash)
transformer_function = ->(key) { key.to_s }
transform_keys(hash, transformer_function)
end
def transform_keys(obj, transformer_function)
case obj
when Array
obj.map{|value| transform_keys(value, transformer_function)}
when Hash
obj.each_with_object({}) do |(key, value), hash|
hash[transformer_function.call(key)] = transform_keys(value, transformer_function)
end
else
obj
end
end
end