Sandra Cieseck
Sandra Cieseck

Reputation: 331

Transform array of nested Hashes into flat array of non-nested hashes

I want to transform the given array into result array:

given = [{
  "foo_v1_4" => [{
    "derivate_version" => 0,
    "layers" => {
      "tlayer" => {
        "baz" => {
          "three" => 0.65
        },
        "bazbar" => {
          "three" => 0.65
        }
      }
    }
  }]
}]


# the value of key :one is first hash key (foo_v1_4) plus underscore (_) plus derivate_version (0)
result = [{
    one: 'foo_v1_4_0',
    tlayer: 'baz',
    three: '0.6'
  },
  {
    one: 'foo_v1_4_0',
    tlayer: 'bazbar',
    three: '0.6'
  }
]

What I tried:

given.each do |el |

    el.each do |derivat |
      derivat.each do |d |
        d.each do |layer |
          layer.each do |l |
            derivat = "#{d}_#{l['derivate_version']}"
          puts derivat
        end
      end
    end
  end

end

I'm struggling at iterating through "layers" hash, the amount of elements in layers is equal to the amount of elements in result array.

Upvotes: 1

Views: 65

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110675

It helps to format the objects so we can better see their structures:

given = [
  {
    "foo_v1_4" => [
      { "derivate_version" => 0,
        "layers" => {
          "tlayer" => {
            "baz" => { "three" => 0.65 },
            "bazbar" => { "three" => 0.65 }
          }
        }
      }
    ]
  }
]
result = [
  {
    one: 'foo_v1_4_0',
    tlayer: 'baz',
    three: '0.6'
  },
  {
    one: 'foo_v1_4_0',
    tlayer: 'bazbar',
    three: '0.6'
  }
]

We can begin by writing the structure of result:

result = [
  {
    one: 
    tlayer:
    three:
  },
  {
    one: 
    tlayer:
    three:
  }
]

We see that

given = [ { "foo_v1_4" => <array> } ]

The values of the keys :one in the hash result[0] is therefore the first key of the first element of given:

one_val = given[0].keys[0] 
  #=> "foo_v1_4"

result = [
  {
    one: one_val
    tlayer:
    three:
  },
  {
    one: one_val
    tlayer:
    three:
  }
]

All the remaining objects of interest are contained in the hash

h = given[0]["foo_v1_4"][0]["layers"]["layer"]
  #=> {
  #     "baz"=>{ "three"=>0.65 },
  #     "bazbar"=>{ "three"=>0.65 }
  #   }

so it is convenient to define it. We see that:

h.keys[0]
  #=> "baz"
h.keys[1]
  #=> "bazaar"
h["bazbar"]["three"]
  #=> 0.65

Note that it generally is not good practice to assume that hash keys are ordered in a particular way.

We may now complete the construction of result,

v = h["bazbar"]["three"].truncate(1)
  #=> 0.6

result = [
  {
    one: one_val,
    tlayer: h.keys[0],
    three: v
  },
  { one: one_val,
    tlayer: h.keys[1],
    three: v
  }
]
  #=> [
  #     { :one=>"foo_v1_4", :tlayer=>"baz",    :three=>0.6 },
  #     { :one=>"foo_v1_4", :tlayer=>"bazbar", :three=>0.6 }
  #   ]

The creation of the temporary objects one_val, h, and v improves time- and space-efficiency, makes the calculations easier to test and improves the readability of the code.

Upvotes: 4

user11350468
user11350468

Reputation: 1407

Try the below:

result = []
given.each do |level1|
  level1.each do |key, derivate_versions|
    derivate_versions.each do |layers|
      # iterate over the elements under tlayer
      layers.dig('layers', 'tlayer').each do |tlayer_key, tlayer_value|
        sub_result = {}
        # key - foo_v1_4, layers['derivate_version'] - 0 => 'foo_v1_4_0'
        sub_result[:one] = key + '_' +  layers['derivate_version'].to_s
        # talyer_key - baz, barbaz
        sub_result[:tlayer] = tlayer_key
        # talyer_value - { "three" => 0.65 }
        sub_result[:three] = tlayer_value['three']
        result << sub_result
      end
    end
  end
end

The value of result will be:

2.6.3 :084 > p result
[{:one=>"foo_v1_4_0", :tlayer=>"baz", :three=>0.65}, {:one=>"foo_v1_4_0", :tlayer=>"bazbar", :three=>0.65}]

Upvotes: 1

Related Questions