How to elegantly symbolize_keys for a 'nested' hash

后端 未结 6 2185
無奈伤痛
無奈伤痛 2021-02-06 20:46

Consider the following code:

  hash1 = {\"one\" => 1, \"two\" => 2, \"three\" => 3}
  hash2 = hash1.reduce({}){ |h, (k,v)| h.merge(k => hash1) }
  ha         


        
6条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2021-02-06 21:05

    You could use:

    • Hash#to_s to convert the hash to a string;
    • String#gsub with a regex to convert the keys from strings to representations of symbols; and then
    • Kernel#eval to convert the string back into a hash.

    This is an easy solution to your problem, but, you should only consider using it if you can trust that eval is not going to produce something nasty. If you have control over the content of the hash being converted, that should not be a problem.

    This approach could be used for other kinds of nested objects, such as ones containing both arrays and hashes.

    Code

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

    Examples

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

    Explanation

    (?==>) in the regex is a zero-width postive lookahead. ?= signifies positive lookahead; => is the string that must immediately follow the match to \"(\w+)\". \1 in ':\1' (or I could have written ":\\1") is a string beginning with a colon followed by a backreference to the content of capture group #1, the key matching \w+ (without the quotes).

提交回复
热议问题