How do I convert hash keys to method names?

前端 未结 5 582
暖寄归人
暖寄归人 2020-12-02 14:37

This is my hash:

tempData = {\"a\" => 100, \"here\" => 200, \"c\" => \"hello\"}

I need to access the hash keys as a method like:

相关标签:
5条回答
  • 2020-12-02 14:43

    you can extend the Hash class in the following way.

    class Hash
          # return nil whenever the key doesn't exist
          def method_missing(m, *opts)
            if self.has_key?(m.to_s)
              return self[m.to_s]
            elsif self.has_key?(m.to_sym)
              return self[m.to_sym]
            end
            return nil
    
            # comment out above line and replace with line below if you want to return an error
            # super
          end
    end
    
    0 讨论(0)
  • 2020-12-02 14:52

    Alternatively, if it’s just a small script it might be more convenient to just extend Hash itself

    class Hash
      def method_missing sym,*
        fetch(sym){fetch(sym.to_s){super}}
      end
    end
    

    method_missing is a magic method that is called whenever your code tries to call a method that does not exist. Ruby will intercept the failing call at run time and let you handle it so your program can recover gracefully. The implementation above tries to access the hash using the method name as a symbol, the using the method name as a string, and eventually fails with Ruby's built-in method missing error.

    NB for a more complex script, where adding this behavior might break other third-party gems, you might alternatively use a module and extend each instance

    module H
      def method_missing sym,*
        fetch(sym){fetch(sym.to_s){super}}
      end
    end
    
    the = { answer: 42 }
    the.extend(H)
    the.answer # => 42
    

    and for greater convenience you can even propagate the module down to nested hashes

    module H
      def method_missing sym,*
        r = fetch(sym){fetch(sym.to_s){super}}
        Hash === r ? r.extend(H) : r
      end
    end 
    
    the = { answer: { is: 42 } }
    the.extend(H)
    the.answer.is # => 42
    
    0 讨论(0)
  • 2020-12-02 14:53

    If the hash is inside a module, you can define methods on that module (or class) dynamically using define_method. For example:

    module Version
      module_function
    
      HASH = { 
        major: 1,
        minor: 2,
        patch: 3,
      }
    
      HASH.each do |name, value|
        define_method(name) do
          return value
        end
      end
    end
    

    This will define a Version module with major, minor, and patch methods that return 1, 2, and 3, respectively.

    0 讨论(0)
  • 2020-12-02 14:58

    There is another way to do this.

    JSON.parse(tempData.to_json, object_class: OpenStruct)

    that will give object #<OpenStruct a=100, here=200, c="hello">

    In this way nested hash also will be converted to OpenStruct Object

    tempData = {a: { b: { c: 3}}, foo: 200, msg: 'test msg'}
    obj = JSON.parse(tempData.to_json, object_class: OpenStruct)
    

    Now we are able to call

    obj.a.b.c # 3
    obj.foo # 200
    obj.msg # 'test msg'
    

    Hope this will help someone.

    0 讨论(0)
  • 2020-12-02 15:04

    You could just wrap up your hash in an OpenStruct:

    require 'ostruct'
    tempData = {"a" => 100, "here" => 200, "c" => "hello"}
    os = OpenStruct.new tempData
    os.a #=> 100
    os.here #=> 200
    

    If you really really wanted to, you could also monkey-patch the Hash class, but I'd advise against that:

    class Hash
      def method_missing(m, *args, &blk)
        fetch(m) { fetch(m.to_s) { super } }
      end
    end
    
    tempData = {"a" => 100, "here" => 200, "c" => "hello"}
    tempData.a #=> 100
    

    Update: In my personal extensions library I added a Hash#to_ostruct method. This will recursively convert a hash into an OpenStruct including all nested hashes.

    0 讨论(0)
提交回复
热议问题