简体   繁体   中英

Join Ruby hash keys into string

I'm trying to create a Ruby template on the fly with Chef attributes but I can't figure out how to map the attributes to output the way I need.

Example Hash:

a = { 
    "route" => { 
        "allocation" => {
            "recovery" => {
                "speed" => 5,
                "timeout" => "30s"
            },
            "converge" => {
                "timeout" => "1m"
            }
        }
    }
}

Would turn into:

route.allocation.recovery.speed: 5
route.allocation.recovery.timeout: 30s
route.allocation.converge.timeout: 1m

Thanks for the help.

You can use recursion if your hash is not large enough to throw stack overflow exception. I don't know what are you trying to achieve, but this is example of how you can do it:

a = { 
    "route" => { 
        "allocation" => {
            "recovery" => {
                "speed" => 5,
                "timeout" => "30s"
            },
            "converge" => {
                "timeout" => "1m"
            }
        }
    }
}

def show hash, current_path = ''
  hash.each do |k,v|
    if v.respond_to?(:each)
      current_path += "#{k}."
      show v, current_path
    else
      puts "#{current_path}#{k} : #{v}"
    end
  end
end

show a

Output:

route.allocation.recovery.speed : 5 
route.allocation.recovery.timeout : 30s
route.allocation.recovery.converge.timeout : 1m 

I don't know Rails but I'm guessing that the following requires only a small tweak to give the result you want:

@result = []
def arrayify(obj, so_far=[])
  if obj.is_a? Hash
    obj.each { |k,v| arrayify(v, so_far+[k]) }
  else
    @result << (so_far+[obj])
  end
end

arrayify(a)
@result
  #=> [["route", "allocation", "recovery", "speed", 5],
  #    ["route", "allocation", "recovery", "timeout", "30s"],
  #    ["route", "allocation", "converge", "timeout", "1m"]] 

EDIT: Did I completely misread your question - is the desired output a string? Oh dear.

I think this is a really good use case for OpenStruct :

require 'ostruct'

def build_structs(a)
  struct = OpenStruct.new
  a.each do |k, v|
    if v.is_a? Hash
      struct[k] = build_structs(v)
    else
      return OpenStruct.new(a)
    end
  end
  struct
end

structs = build_structs(a)

output:

[2] pry(main)> structs.route.allocation.recovery.speed
=> 5

For anyone wanting to convert an entire hash with multi levels then here is the code I ended up using:

confHash = { 
  'elasticsearch' => {
    'config' => {
      'discovery' => {
        'zen' => {
          'ping' => {
            'multicast' => {
              'enabled' => false
            },
            'unicast' => {
              'hosts' => ['127.0.0.1']
            }
          }
        }
      }
    }
  }
}


def generate_config( hash, path = [], config = [] )
  hash.each do |k, v|
    if v.is_a? Hash
      path << k
      generate_config( v, path, config )
    else
      path << k
      if v.is_a? String
        v = "\"#{v}\""
      end
      config << "#{path.join('.')}: #{v}"
    end
    path.pop
  end
  return config
end


puts generate_config(confHash['elasticsearch']['config'])

# discovery.zen.ping.multicast.enabled: false
# discovery.zen.ping.unicast.hosts: ["127.0.0.1"]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM