将字符串转换为哈希符号的最佳方法

在Ruby中,将哈希中的所有键从字符串转换为符号的(最快/最干净/直接)方法是什么?

这在解析YAML时非常方便。

my_hash = YAML.load_file('yml')

我希望能够使用:

my_hash[:key]

而不是:

my_hash['key']
222215 次浏览

会像下面这样工作吗?

new_hash = Hash.new
my_hash.each { |k, v| new_hash[k.to_sym] = v }

它会复制哈希值,但大多数时候你不会在意。可能有一种不复制所有数据的方法。

你可以偷懒,把它包装在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}

所以要注意的是在两种键形式之间切换。坚持用一个。

Ruby >= 2.5 (文档)中,您可以使用:

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

如果你在使用Rails,这里有一个更好的方法:

params.symbolize_keys

最后。

如果你不是,那就抄袭他们的代码(链接中也有):

myhash.keys.each do |key|
myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end

我真的很喜欢土豆泥宝石。

你可以执行mash['key'],或mash[:key],或mash.key

对于Ruby中YAML的特定情况,如果键以':'开头,它们将自动被作为符号存储。

require 'yaml'
require 'pp'
yaml_str = "
connections:
- host: host1.example.com
port: 10000
- host: host2.example.com
port: 20000
"
yaml_sym = "
:connections:
- :host: host1.example.com
:port: 10000
- :host: host2.example.com
:port: 20000
"
pp yaml_str = YAML.load(yaml_str)
puts yaml_str.keys.first.class
pp yaml_sym = YAML.load(yaml_sym)
puts yaml_sym.keys.first.class

输出:

#  /opt/ruby-1.8.6-p287/bin/ruby ~/test.rb
{"connections"=>
[{"port"=>10000, "host"=>"host1.example.com"},
{"port"=>20000, "host"=>"host2.example.com"}]}
String
{:connections=>
[{:port=>10000, :host=>"host1.example.com"},
{:port=>20000, :host=>"host2.example.com"}]}
Symbol

这个怎么样:

my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))


# my_hash['key'] => "val"
# my_hash[:key]  => "val"

params.symbolize_keys也可以工作。这个方法将哈希键转换为符号并返回一个新的哈希值。

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}

如果你使用的是Rails,这就简单多了——你可以使用HashWithIndifferentAccess,并以字符串和符号的形式访问键:

my_hash.with_indifferent_access

参见:

http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html


或者你可以使用很棒的“Facets of Ruby”Gem,它包含了很多对Ruby核心和标准库类的扩展。

  require 'facets'
> {'some' => 'thing', 'foo' => 'bar'}.symbolize_keys
=>  {:some=>"thing", :foo=>"bar}
< p >参见: http://rubyworks.github.io/rubyfaux/?doc=http://rubyworks.github.io/facets/docs/facets-2.9.3/core.json#api-class-Hash < / p >

更简洁的是:

Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]

这是一种深度象征物体的方法

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

@igorsales answer的修改

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

更短的一行代码:

my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }

http://api.rubyonrails.org/classes/Hash.html#method-i-symbolize_keys

hash = { 'name' => 'Rob', 'age' => '28' }
hash.symbolize_keys
# => { name: "Rob", age: "28" }

这并不完全是一行程序,但是它将所有字符串键转换为符号,包括嵌套的符号:

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

我喜欢这一行,当我不使用Rails时,因为这样我就不必在处理它时进行第二个哈希并持有两组数据:

my_hash = { "a" => 1, "b" => "string", "c" => true }


my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key) }


my_hash
=> {:a=>1, :b=>"string", :c=>true}

哈希#delete返回已删除键的值

我们想要改变的数组。

strings = ["HTML", "CSS", " JavaScript", "Python", "Ruby"]

创建一个新变量作为空数组,这样我们就可以“push”;符号在。

Symbols = []

在这里,我们定义了一个带有块的方法。

字符串。每个{|x| symbols.push(x.intern)}

代码结束。

因此,这可能是在Ruby中将字符串转换为数组中的符号的最直接的方法。创建一个字符串数组,然后创建一个新变量,并将该变量设置为空数组。然后选择使用".each"创建的第一个数组中的每个元素;方法。然后使用分组代码“push”;输入新数组中的所有元素,并使用“;实习生”或“。to_sym_”;将所有元素转换为符号。

符号更快,因为它们在代码中节省了更多的内存,并且只能使用一次。符号是哈希中最常用的键,这很好。我不是最好的ruby程序员,但这种形式的代码帮助了我很多。如果有人知道更好的方法,请分享,你也可以使用这个方法哈希!

这适用于那些使用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
  • symbolic也是递归包含的散列
  • 返回符号化的哈希值
  • 工作到位!

如果你想要香草红宝石解决方案,因为我没有访问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

Symbolize_keys递归为任何哈希:

class Hash
def symbolize_keys
self.is_a?(Hash) ? Hash[ self.map { |k,v| [k.respond_to?(:to_sym) ? k.to_sym : k, v.is_a?(Hash) ? v.symbolize_keys : v] } ] : self
end
end

这里有很多答案,但rails函数的一个方法是hash.symbolize_keys

这是嵌套哈希的一行代码

def symbolize_keys(hash)
hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v }
end

方面的散列# deep_rekey也是一个不错的选择,特别是:

  • 如果你在项目中发现了其他糖的用途,
  • 如果您更喜欢代码可读性而不是神秘的一行程序。

示例:

require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey

如果你需要这样做的原因是因为你的数据最初来自JSON,你可以跳过任何解析,只需在摄取JSON时传入:symbolize_names选项。

不需要Rails和工作与Ruby >1.9

JSON.parse(my_json, :symbolize_names => true)

在ruby中,我发现这是最简单、最容易理解的将字符串键转换为符号的方法:

my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)}

对于散列中的每个键,我们调用delete函数将其从散列中删除(也可以删除已删除的返回与该键关联的值),并立即将其设置为符号化的键。

在Rails中,你可以使用:

{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!

转换:

{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}

因为Ruby 2.5.0,你可以使用Hash#transform_keysHash#transform_keys!

{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}

如果你正在使用json,并且想要使用它作为哈希,在核心Ruby中你可以这样做:

json_obj = JSON.parse(json_str, symbolize_names: true)

symbolize_names:如果设置为true,则返回JSON对象中名称(键)的符号。否则返回字符串。字符串是默认的。

道格:Json #解析symbolize_names

类似于前面的解决方案,但写得有点不同。

  • 这允许嵌套和/或具有数组的散列。
  • 获得键到字符串的转换作为奖励。
  • 代码不会改变传入的哈希值。

    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
    

心理 3.0开始,你可以添加symbolize_names:选项

< p > <代码>心理。加载("——\n foo: bar") # =比;{“foo”=比;“酒吧”}< /代码> < / p > < p > <代码>心理。加载("——\n foo: bar", symbolize_names: true) # =比;{: foo =比;“酒吧”}< /代码> < / p >

注意:如果你的Psych版本低于3.0,symbolize_names:将被默默地忽略。

我的Ubuntu 18.04自带ruby 2.5.1p57