0

I want to create a nested hash using four values type, name, year, value. ie, key of the first hash will be type, value will be another hash with key name, then value of that one will be another hash with key year and value as value.

The array of objects I'm iterating looks like this:

elements = [
    {
        year: '2018',
        items: [
            {
                name: 'name1',
                value: 'value1',
                type: 'type1',
            },
            {
                name: 'name2',
                value: 'value2',
                type: 'type2',
            },
        ]
    },
    {
        year: '2019',
        items: [
            {
                name: 'name3',
                value: 'value3',
                type: 'type2',
            },
            {
                name: 'name4',
                value: 'value4',
                type: 'type1',
            },
        ]
    }
]

And I'm getting all values together using two loops like this:

elements.each do |element|
    year = element.year
    element.items.each |item|
        name = item.name
        value = item.value
        type = item.type

        # TODO: create nested hash
    end
end

Expected output is like this:

{
    "type1" => {
        "name1" => {
            "2018" => "value1"
        },
        "name4" => {
            "2019" => "value4"
        }
    },
    "type2" => {
        "name2" => {
            "2018" => "value2"
        },
        "name3" => {
            "2019" => "value3"
        }
    }
}

I tried out some methods but it doesn't seems to work out as expected. How can I do this?

3
  • Can you please add your tried solutions Commented May 22, 2019 at 6:27
  • Your JSON is malformed. Commented May 22, 2019 at 6:29
  • @AlekseiMatiushkin actually it is an array of objects, just corrected it Commented May 22, 2019 at 6:38

2 Answers 2

4
elements.each_with_object({}) { |g,h| g[:items].each { |f|
    h.update(f[:type]=>{ f[:name]=>{ g[:year]=>f[:value] } }) { |_,o,n| o.merge(n) } } }
  #=> {"type1"=>{"name1"=>{"2018"=>"value1"}, "name4"=>{"2019"=>"value4"}},
  #    "type2"=>{"name2"=>{"2018"=>"value2"}, "name3"=>{"2019"=>"value3"}}} 

This uses the form of Hash#update (aka merge!) that employs a block (here { |_,o,n| o.merge(n) } to determine the values of keys that are present in both hashes being merged. See the doc for definitions of the three block variables (here _, o and n). Note that in performing o.merge(n) o and n will have no common keys, so a block is not needed for that operation.

Sign up to request clarification or add additional context in comments.

2 Comments

Good one! I wonder when will we have Hash#bury or like to avoid this ugly fence {=>{=>{=>{=> :)
@AlekseiMatiushkin: class Hash; def bury(*keys, val); keys.size > 1 ? self.dig(*keys[0..-2])[keys[-1]] = val : self[keys.first] = val; end; end (the hash better be autovivifying, though, or it won't be too useful),
2

Assuming you want to preserve the references (unlike in your desired output,) here you go:

elements = [
  {
    year: '2018',
    items: [
      {name: 'name1', value: 'value1', type: 'type1'},
      {name: 'name2', value: 'value2', type: 'type2'}
    ]
},
{
    year: '2019',
    items: [
      {name: 'name3', value: 'value3', type: 'type2'},
      {name: 'name4', value: 'value4', type: 'type1'}
    ]
  }
]

Just iterate over everything and reduce into the hash. On the structures of known shape is’s a trivial task:

elements.each_with_object(
    Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) } # for deep bury
) do |h, acc|
  h[:items].each do |item|
    acc[item[:type]][item[:name]][h[:year]] = item[:value]
  end
end

#⇒ {"type1"=>{"name1"=>{"2018"=>"value1"},
#             "name4"=>{"2019"=>"value4"}},
#   "type2"=>{"name2"=>{"2018"=>"value2"},
#             "name3"=>{"2019"=>"value3"}}}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.