{ "tasks"=>[ { "arguments"=>{ "parameters"=>[ { "name"=>"inputJson", "value"=>"{{workflow." /> { "tasks"=>[ { "arguments"=>{ "parameters"=>[ { "name"=>"inputJson", "value"=>"{{workflow." /> { "tasks"=>[ { "arguments"=>{ "parameters"=>[ { "name"=>"inputJson", "value"=>"{{workflow."/>

Accessing Nested Hash Elements in Ruby

64 views Asked by At

I have an ruby array that looks like this.

arr = [
  { "dag"=>{
      "tasks"=>[
        { "arguments"=>{
            "parameters"=>[
              { "name"=>"inputJson",
                "value"=>"{{workflow.parameters.inputJson}}"
              }
            ]
          },
          "name"=>"build-handler-workflow",
          "template"=>"build-handler-workflow"
        }
      ]
    },
    "name"=>"dag-build-handler-workflow"
  },
  { "container"=>{
      "args"=>[
        "{{inputs.parameters.inputJson}}"
      ],
      "command"=>["cowsay"],
      "image"=>"docker/whalesay:latest",
      "resources"=>{
        "requests"=>{
          "cpu"=>"100m",
          "memory"=>"100Mi"
        }
      }
    },
    "inputs"=>{
      "parameters"=>[
        {"name"=>"inputJson"}
      ]
    },
    "metadata"=>{
      "annotations"=>{
        "workflows.argoproj.io/progress"=>"0/10000"
      }
    },
    "name"=>"build-handler-workflow",
    "synchronization"=>{
      "semaphore"=>{
        "configMapKeyRef"=>{
          "key"=>"template",
          "name"=>"build-handler-config"
        }
      }
    }
  }
]

How would I go about iterating through this array hash so I can get only image field. may be something like this

p arr['spec']['templates']['container']['image']
"image"=>"docker/whalesay:latest"

I have Tried map method to access hash values. not able to get valid results.

p arr.map{|e|"#{e["container"]}"}
["", "{\"args\"=>[\"{{inputs.parameters.inputJson}}\"], \"command\"=>[\"cowsay\"], \"image\"=>\"docker/whalesay:latest\", \"resources\"=>{\"requests\"=>{\"cpu\"=>\"100m\", \"memory\"=>\"100Mi\"}}}"]
4

There are 4 answers

0
Rajagopalan On
arr=[{"dag"=>{"tasks"=>[{"arguments"=>{"parameters"=>[{"name"=>"inputJson", "value"=>"{{workflow.parameters.inputJson}}"}]}, "name"=>"build-handler-workflow", "template"=>"build-handler-workflow"}]}, "name"=>"dag-build-handler-workflow"}, {"container"=>{"args"=>["{{inputs.parameters.inputJson}}"], "command"=>["cowsay"], "image"=>"docker/whalesay:latest", "resources"=>{"requests"=>{"cpu"=>"100m", "memory"=>"100Mi"}}}, "inputs"=>{"parameters"=>[{"name"=>"inputJson"}]}, "metadata"=>{"annotations"=>{"workflows.argoproj.io/progress"=>"0/10000"}}, "name"=>"build-handler-workflow", "synchronization"=>{"semaphore"=>{"configMapKeyRef"=>{"key"=>"template", "name"=>"build-handler-config"}}}}]

Code

arr.each do |hash|
  if hash['container']
    puts hash['container']['image']
  end
end

Or you could use dig method

arr.each { |hash| puts hash.dig('container', 'image') if hash['container'] }

Output

docker/whalesay:latest
2
Sebas On

You can use this, also dig is pretty useful when navigating through hashes

images = []
arr.each do |item|
  img = item.dig("container", "image")
  images << img unless img.nil?
end

p images
0
Cary Swoveland On

If you don't know in which nested hashes in a given array or hash (here an array) a given key can be found, and want to return an array containing the values of that key throughout the given object, you can use the following recursive method.

def doit(obj, key)
  case obj
  when Array
    obj.each_with_object([]) do |o, arr|
      if [Array, Hash].include?(o.class)
        res = doit(o, key)
        res.each { |v| arr << v }
      end  
    end
  when Hash
    obj.each_with_object([]) do |(k,v), arr|
      if k == key
        arr << v
      elsif [Array, Hash].include?(v.class)
        res = doit(v, key)
        res.each { |v| arr << v }
      end
    end
  end
end
doit(arr, "image")
  #=> ["docker/whalesay:latest"]
doit(arr, "name")
  #=> ["inputJson", "build-handler-workflow", "dag-build-handler-workflow",
  #    "inputJson", "build-handler-workflow", "build-handler-config"]
doit(arr[0]["dag"]["tasks"], "name")
  #=> ["inputJson", "build-handler-workflow"]
doit(arr, "inputs")
  #=> [{"parameters"=>[{"name"=>"inputJson"}]}]
0
Amey On

Thank you so much for providing all of these alternatives, All of these methods works fine but I prefer this one:

images = arr.filter_map { |e| e.dig("container", "image") }

I am able to get the data from array.