273

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

这在解析 YAML 时会很方便。

my_hash = YAML.load_file('yml')

我希望能够使用:

my_hash[:key] 

而不是:

my_hash['key']
4

31 回答 31

316

如果您使用的是 Rails,这是一个更好的方法:

参数。符号化键

结束。

如果你不是,只需撕掉他们的代码(它也在链接中):

myhash.keys.each do |key|
  myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end
于 2009-05-21T00:14:58.493 回答
305

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 
于 2009-04-29T01:17:06.357 回答
115

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

需要'yaml'
需要'pp'
yaml_str = "
连接:
  - 主机:host1.example.com
    端口:10000
  - 主机:host2.example.com
    端口:20000
"
yaml_sym = "
:连接:
  - :主机:host1.example.com
    :端口:10000
  - :主机: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"},
   {"port"=>20000, "host"=>"host2.example.com"}]}
细绳
{:连接=>
  [{:port=>10000, :host=>"host1.example.com"},
   {:port=>20000, :host=>"host2.example.com"}]}
象征
于 2009-12-18T07:48:41.520 回答
65

如果您使用的是 Rails,它会简单得多 - 您可以使用 HashWithIndifferentAccess 并以字符串和符号的形式访问键:

my_hash.with_indifferent_access 

也可以看看:

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


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

  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

于 2011-08-27T06:02:35.843 回答
64

更简洁:

Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]
于 2012-04-10T14:20:00.793 回答
58

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

{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}
于 2018-01-22T00:15:30.137 回答
32

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

hash = { 'name' => 'Rob', 'age' => '28' }
hash.symbolize_keys
# => { name: "Rob", age: "28" }
于 2014-04-23T12:06:41.120 回答
27

这是一种深度符号化对象的方法

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
于 2012-08-03T01:30:28.997 回答
26

如果您使用的是 json,并且想将其用作哈希,那么在核心 Ruby 中您可以这样做:

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

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

文档:Json#parse symbolize_names

于 2018-05-04T11:19:05.663 回答
21

我真的很喜欢Mash宝石。

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

于 2009-05-21T00:27:46.923 回答
13

params.symbolize_keys也将工作。此方法将散列键转换为符号并返回一个新的散列。

于 2010-12-31T05:43:06.440 回答
12

对@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
于 2013-04-04T15:32:18.213 回答
12

在 Rails 中,您可以使用:

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

转换为:

{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
于 2017-10-31T10:40:18.357 回答
8

这里有很多答案,但一种方法 rails 功能是hash.symbolize_keys

于 2016-05-19T22:30:02.190 回答
8

这是我的嵌套哈希的一个衬里

def symbolize_keys(hash)
  hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v }
end
于 2016-08-19T17:05:51.057 回答
7

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

不需要 Rails 并且适用于 Ruby >1.9

JSON.parse(my_json, :symbolize_names => true)
于 2017-07-16T22:55:07.770 回答
5

您可能很懒惰,并将其包装在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}

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

于 2009-04-28T23:20:14.843 回答
4

会像以下工作吗?

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

它会复制哈希,但大多数时候你不会关心它。可能有一种方法可以在不复制所有数据的情况下做到这一点。

于 2009-04-28T22:49:35.133 回答
4

更短的单线 fwiw:

my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
于 2014-01-16T15:34:00.977 回答
3

这个怎么样:

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

# my_hash['key'] => "val"
# my_hash[:key]  => "val"
于 2010-12-17T13:57:05.233 回答
3

这适用于使用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 也递归包含哈希
  • 返回符号化的哈希
  • 工作到位!
于 2015-10-24T13:23:02.730 回答
2

我们要更改的数组。

字符串 = [“HTML”、“CSS”、“JavaScript”、“Python”、“Ruby”]

创建一个新变量作为空数组,以便我们可以“.push”其中的符号。

符号 = [ ]

这是我们用块定义方法的地方。

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

代码结束。

所以这可能是在 Ruby 中将字符串转换为数组中符号的最直接的方法。创建一个字符串数组,然后创建一个新变量并将该变量设置为一个空数组。然后选择您使用“.each”方法创建的第一个数组中的每个元素。然后使用块代码“.push”新数组中的所有元素,并使用“.intern 或 .to_sym”将所有元素转换为符号。

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

于 2015-07-05T08:20:01.107 回答
2

如果您想要香草红宝石解决方案并且因为我无法访问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
于 2015-11-27T09:26:19.963 回答
2

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

于 2018-11-22T00:57:04.650 回答
1
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}
于 2011-07-24T09:41:00.193 回答
1

这不完全是单行的,但它将所有字符串键转换为符号,也是嵌套的:

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
于 2015-06-03T06:02:09.290 回答
1

当我不使用 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}

Hash#delete 返回被删除键的值

于 2015-07-02T16:05:49.213 回答
1

Facets 的 Hash#deep_rekey也是一个不错的选择,尤其是:

  • 如果您发现项目中的其他糖分方面有用,
  • 如果您更喜欢代码可读性而不是神秘的单行代码。

样本:

require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey
于 2016-11-02T07:12:01.357 回答
1

在 ruby​​ 中,我发现这是将哈希中的字符串键转换为符号的最简单易懂的方法:

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

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

于 2017-08-02T04:02:51.197 回答
1

与以前的解决方案类似,但编写方式略有不同。

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

    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
    
于 2018-10-17T01:29:58.377 回答
-1

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
于 2016-02-16T14:11:53.520 回答