代码之家  ›  专栏  ›  技术社区  ›  SHS

如何优雅地符号化“嵌套”哈希的_keys

  •  67
  • SHS  · 技术社区  · 12 年前

    考虑以下代码:

      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是一个“嵌套”的哈希,即具有字符串键和类似“嵌套”哈希值的哈希。

    Rails中Hash的“symbolize_keys”方法使我们可以轻松地将字符串键转换为符号。但我在找一个 优雅的 将所有键(主键加上hash4中所有哈希的键)转换为符号的方法。

    重点是从我的( 国际海事组织 )丑陋的解决方案:

      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
    

    仅供参考:设置为Rails 3.2.17和Ruby 2.1.1

    更新:

    答案是 hash4.deep_symbolize_keys 对于轨道<=5

    答案是 JSON.parse(JSON[hash4], symbolize_names: true) 对于轨道>5.

    6 回复  |  直到 10 年前
        1
  •  129
  •   jvnill    7 年前

    有几种方法可以做到这一点

    1. 有一个 deep_symbolize_keys method in Rails

      hash.deep_symbolize_keys!

    2. 正如@chrisgeeq所提到的 deep_transform_keys Rails4提供的方法。

      hash.deep_transform_keys(&:to_sym)

      还有一声巨响 ! 版本以替换现有对象。

    3. 还有一种方法叫做 with_indifferent_access 。这允许您使用字符串或符号访问哈希,如 params 在控制器中。此方法没有bang对应项。

      hash = hash.with_indifferent_access

    4. 最后一个是使用 JSON.parse 我个人不喜欢这样做,因为你要做两个转换-hash到json,然后json到hash。

      JSON.parse(JSON[h], symbolize_names: true)

    更新:

    19年1月16日-添加更多选项并注意弃用deep_symbolize_keys

    2012年4月19日-删除已弃用的注释。只有方法中使用的实现被弃用,而不是方法本身。

        2
  •  28
  •   Mikhail Chuprynski    10 年前

    不能将此方法用于params或 ActionController::Parameters 更多,因为 deep_symbolize_keys 由于安全原因,该方法在Rails 5.0+中已弃用,并将在Rails 5.1+中删除 ActionController::参数 不再 从哈希继承

    因此,@Uri Agassi的这种方法 似乎是普遍的 一

    JSON.parse(JSON[h], symbolize_names: true)
    

    然而,Rails Hash对象仍然有它。

    因此,选项如下:

    • 如果你不使用Rails或者只是不在乎:

      JSON.parse(JSON[h],symbolize_names:true)
      
    • 使用Rails和ActionController::参数:

      params.to_unsafe_h.deep_symbolize_keys
      
    • 使用Rails和普通哈希

      h.deep_symbolize_keys
      
        3
  •  6
  •   Alexander    11 年前

    在rails中,您可以创建HashWithIndidifferentAccess类。创建一个此类的实例,将哈希传递给其构造函数,然后使用符号或字符串(如Controller的Actions的参数)键访问该类:

    hash = {'a' => {'b' => [{c: 3}]}}
    
    hash = hash.with_indifferent_access
    # equal to:
    # hash = ActiveSupport::HashWithIndifferentAccess.new(hash)
    
    hash[:a][:b][0][:c]
    
    => 3
    
        4
  •  3
  •   Uri Agassi    12 年前

    我可以这样建议:

    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} 
    

    您还可以轻松地将其扩展到支持 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"}]}
    
        5
  •  2
  •   mirage    7 年前

    我可以建议:

    JSON.parse(hash_value.to_json)
    
        6
  •  1
  •   Cary Swoveland    12 年前

    您可以使用:

    这是一个解决问题的简单方法,但是,只有当你能够相信这一点时,你才应该考虑使用它 eval 不会产生令人讨厌的东西。如果您可以控制正在转换的哈希的内容,这应该不是问题。

    这种方法可以用于其他类型的嵌套对象,例如同时包含数组和散列的对象。

    密码

    def symbolize_hash(h)
      eval(h.to_s.gsub(/\"(\w+)\"(?==>)/, ':\1'))
    end
    

    示例

    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"}]}
    

    解释

    (?==>) 在正则表达式中,是一个零宽度的正向前瞻。 ?= 表示积极展望; => 必须紧跟在匹配项之后的字符串 \"(\w+)\" . \1 在里面 ':\1' (或者我可以写 ":\\1" )是一个以冒号开头的字符串,后跟对捕获组#1内容的反向引用,密钥匹配 \w+ (没有引号)。