代码之家  ›  专栏  ›  技术社区  ›  Bryan M.

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

  •  225
  • Bryan M.  · 技术社区  · 16 年前

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

    这在解析YAML时非常方便。

    my_hash = YAML.load_file('yml')
    

    my_hash[:key] 
    

    而不是:

    my_hash['key']
    
    31 回复  |  直到 10 年前
        1
  •  312
  •   Jellicle    12 年前

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

    params。 symbolize_keys

    结束。

    如果你不是,只需剽窃他们的代码(链接中也有):

    myhash.keys.each do |key|
      myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
    end
    
        2
  •  292
  •   mattes    5 年前

    在里面 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}
    

    具有 轨道

    my_hash.symbolize_keys
    my_hash.deep_symbolize_keys 
    
        3
  •  115
  •   the Tin Man    11 年前

    对于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
    
        4
  •  64
  •   Michael Barton    12 年前

    Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]
    
        5
  •  63
  •   Tilo    9 年前

    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

        6
  •  57
  •   Sagar Pandya    7 年前

    自从 Ruby 2.5.0 Hash#transform_keys Hash#transform_keys! .

    {'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}
    
        7
  •  32
  •   Ery    11 年前

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

    hash = { 'name' => 'Rob', 'age' => '28' }
    hash.symbolize_keys
    # => { name: "Rob", age: "28" }
    
        8
  •  27
  •   igorsales    12 年前

    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
    
        9
  •  24
  •   Mark    7 年前

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

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

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

    文件: Json#parse symbolize_names

        10
  •  21
  •   ykaganovich Mike Samuel    16 年前

    我真的很喜欢它 Mash 宝石

    mash['key'] mash[:key] mash.key

        11
  •  13
  •   the Tin Man    11 年前

    params.symbolize_keys

        12
  •  12
  •   mattes    5 年前

    在Rails中,您可以使用:

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

    {:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
    
        13
  •  11
  •   Tony    12 年前

    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
    
        14
  •  8
  •   shakirthow    9 年前

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

        15
  •  8
  •   Nick Dobson    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
    
        16
  •  7
  •   Adam Grant    7 年前

    万一 原因 :symbolize_names 接收JSON时的选项。

    JSON.parse(my_json, :symbolize_names => true)
    
        17
  •  5
  •   rampion    16 年前

    你可以偷懒,把它包在一个袋子里 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}
    

        18
  •  4
  •   ChrisInEdmonton    16 年前

    你想做以下工作吗?

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

    它会复制散列,但大多数时候你不会在意。可能有一种方法可以做到这一点,而无需复制所有数据。

        19
  •  4
  •   sensadrome    11 年前

    较短的单衬里fwiw:

    my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
    
        20
  •  3
  •   Fredrik Boström    14 年前

    my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))
    
    # my_hash['key'] => "val"
    # my_hash[:key]  => "val"
    
        21
  •  3
  •   Matteo Ragni    7 年前

    这是为使用 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
    • 也可以递归地表示包含的哈希
    • 返回符号化的散列
    • 工作到位!
        22
  •  2
  •   Community CDub    4 年前

    我们要更改的数组。

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

    将一个新变量设为空数组,这样我们就可以“.push”符号。

    符号=[]

    strings.each{x | symbols.push(x.intern)}

    代码结束。

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

        23
  •  2
  •   Haris Krajina    9 年前

    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
    
        24
  •  2
  •   Rodrigo Estebanez    6 年前

    心理 3.0您可以添加 symbolize_names: 选项

    Psych.load("---\n foo: bar") # => {"foo"=>"bar"}

    Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}

    注意:如果你的心理版本低于3.0 symbolize_names: 将被默默忽略。

    我的Ubuntu 18.04包含了ruby 2.5.1p57的开箱即用功能

        25
  •  1
  •   Vlad Khomich    13 年前
    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}
    
        26
  •  1
  •   Cjoerg    10 年前

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

    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
    
        27
  •  1
  •   nevets138    9 年前

    当我不使用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返回已删除密钥的值

        28
  •  1
  •   Sergikon    8 年前

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

    • 如果您喜欢代码的可读性而不是晦涩难懂的一行代码。

    样本:

    require 'facets/hash/deep_rekey'
    my_hash = YAML.load_file('yml').deep_rekey
    
        29
  •  1
  •   user4488109 user4488109    7 年前

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

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

    返回与键关联的值 我们立即将其设置为符号化键。

        30
  •  1
  •   jham    6 年前

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

    • 将密钥转换为字符串作为奖励。
    • 代码不会对传入的哈希进行变异。

      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