简体   繁体   English

使用Ruby中的循环创建深度嵌套的哈希

[英]Create a deep nested hash using loops in Ruby

I want to create a nested hash using four values type , name , year , value . 我想使用四个值typenameyearvalue创建嵌套哈希。 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 . 即,第一个哈希的键将是type ,value将是另一个具有键name哈希,然后那个哈希的值将是另一个key year和value为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? 我怎样才能做到这一点?

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. 这使用Hash#update (也称为merge! )的形式,该形式采用一个块(此处为{ |_,o,n| o.merge(n) }来确定要合并的两个哈希中都存在的键的值。定义三个块变量(此处为_on )的文档,请注意,在执行o.merge(n) on将没有公共键,因此该操作不需要块。

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"}}}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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