Home > Mobile >  How to pass method arguments use as Hash path?
How to pass method arguments use as Hash path?

Time:09-16

E.G.

def do_the_thing(file_to_load, hash_path)
  file = File.read(file)
  data = JSON.parse(file, { symbolize_names: true })
  data[sections.to_sym]
end

do_the_thing(file_I_want, '[:foo][:bar][0]')

Tried a few methods but failed so far.

Thanks for any help in advance :)

CodePudding user response:

Assuming you missed the parameters names...

Lets assume our file is:

// test.json
{
  "foo": {
    "bar": ["foobar"]
  }
}

Recomended solution

Does your param really need to be a string?? If your code can be more flexible, and pass arguments as they are on ruby, you can use the Hash dig method:

require 'json'

def do_the_thing(file, *hash_path)
  file = File.read(file)
  data = JSON.parse(file, symbolize_names: true)
  data.dig(*hash_path)
end

do_the_thing('test.json', :foo, :bar, 0)

You should get

"foobar"

It should work fine !!


Read the rest of the answer if that doesn't satisfy your question


Alternative solution (using the same argument)

If you REALLY need to use that argument as string, you can;

Treat your params to adapt to the first solution, it won't be a small or fancy code, but it will work:

require 'json'

BRACKET_REGEX = /(\[[^\[]*\])/.freeze

# Treats the literal string to it's correspondent value
def treat_type(param)
  # Remove the remaining brackets from the string
  # You could do this step directly on the regex if you want to
  param = param[1..-2]
  case param[0]
  # Checks if it is a string
  when '\''
    param[1..-2]
  # Checks if it is a symbol
  when ':'
    param[1..-1].to_sym
  else
    begin
      Integer(param)
    rescue ArgumentError
      param
    end
  end
end

# Converts your param to the accepted pattern of 'dig' method
def string_to_args(param)
  # Scan method will break the match results of the regex into an array
  param.scan(BRACKET_REGEX).flatten.map { |match| treat_type(match) }
end

def do_the_thing(file, hash_path)
  hash_path = string_to_args(hash_path)
  file = File.read(file)
  data = JSON.parse(file, symbolize_names: true)
  data.dig(*hash_path)
end

so:

do_the_thing('test.json', '[:foo][:bar][0]')

returns

"foobar"

This solution though is open to bugs when the "hash_path" is not on an acceptable pattern, and treating it's bugs might make the code even longer


Shortest solution (Not safe)

You can use Kernel eval method which I EXTREMELY discourage to use for security reasons, read the documentation and understand its danger before using it

require 'json'

def do_the_thing(file, hash_path)
  file = File.read(file)
  data = JSON.parse(file, symbolize_names: true)
  eval("data#{hash_path}")
end

do_the_thing('test.json', '[:foo][:bar][0]')

CodePudding user response:

If the procedure you were trying to work with was just extracting the JSON data to an object, you might find yourself using either of the following scenarios:

def do_the_thing(file_to_load)
  file = File.read(file)
  data = JSON.parse(file, { symbolize_names: true })
  data[sections.to_sym]
end

do_the_thing(file_I_want)[:foo][:bar][0]

or use the dig function of Hash :

def do_the_thing(file_to_load, sections)
  file = File.read(file)
  data = JSON.parse(file, { symbolize_names: true })
  data.dig(*sections)
end

do_the_thing(file_I_want, [:foo, :bar, 0])
  •  Tags:  
  • ruby
  • Related