Iterate over a deeply nested level of hashes in Ruby

后端 未结 8 1755
眼角桃花
眼角桃花 2020-12-05 00:13

So I have a hash, and for each level of the hash, I want to store its key and value. The problem is, a value can be another hash array. Furthermore, that hash can contain ke

8条回答
  •  生来不讨喜
    2020-12-05 00:39

    Here is recursive (read improved) version of Hash::each(Hash::each_pair) with block and enumerator support:

    module HashRecursive
        refine Hash do
            def each(recursive=false, &block)
                if recursive
                    Enumerator.new do |yielder|
                        self.map do |key, value|
                            value.each(recursive=true).map{ |key_next, value_next| yielder << [[key, key_next].flatten, value_next] } if value.is_a?(Hash)
                            yielder << [[key], value]
                        end
                    end.entries.each(&block)
                else
                    super(&block)
                end
            end
            alias_method(:each_pair, :each)
        end
    end
    
    using HashRecursive
    

    Here are usage examples of Hash::each with and without recursive flag:

    hash = {
        :a => {
            :b => {
                :c => 1,
                :d => [2, 3, 4]
            },
            :e => 5
        },
        :f => 6
    }
    
    p hash.each, hash.each {}, hash.each.size
    # #{:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}, :f=>6}:each>
    # {:a=>{:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}, :f=>6}
    # 2
    
    p hash.each(true), hash.each(true) {}, hash.each(true).size
    # #1, :d=>[2, 3, 4]}], [[:a, :e], 5], [[:a], {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}], [[:f], 6]]:each>
    # [[[:a, :b, :c], 1], [[:a, :b, :d], [2, 3, 4]], [[:a, :b], {:c=>1, :d=>[2, 3, 4]}], [[:a, :e], 5], [[:a], {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}], [[:f], 6]]
    # 6
    
    hash.each do |key, value|
        puts "#{key} => #{value}"
    end
    # a => {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}
    # f => 6
    
    hash.each(true) do |key, value|
        puts "#{key} => #{value}"
    end
    # [:a, :b, :c] => 1
    # [:a, :b, :d] => [2, 3, 4]
    # [:a, :b] => {:c=>1, :d=>[2, 3, 4]}
    # [:a, :e] => 5
    # [:a] => {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}
    # [:f] => 6
    
    hash.each_pair(recursive=true) do |key, value|
        puts "#{key} => #{value}" unless value.is_a?(Hash)
    end
    # [:a, :b, :c] => 1
    # [:a, :b, :d] => [2, 3, 4]
    # [:a, :e] => 5
    # [:f] => 6
    

    Here is example from the question itself:

    hash = {
        :key1   =>  'value1',
        :key2   =>  'value2',
        :key3   =>  {
            :key4   =>  'value4',
            :key5   =>  'value5'
        },
        :key6   =>  {
            :key7   =>  'value7',
            :key8   =>  {
                :key9   =>  'value9'
            }
        }
    }
    
    hash.each_pair(recursive=true) do |key, value|
        puts "#{key} => #{value}" unless value.is_a?(Hash)
    end
    # [:key1] => value1
    # [:key2] => value2
    # [:key3, :key4] => value4
    # [:key3, :key5] => value5
    # [:key6, :key7] => value7
    # [:key6, :key8, :key9] => value9
    

    Also take a look at my recursive version of Hash::merge(Hash::merge!) here.

提交回复
热议问题