Ruby: Get all keys in a hash (including sub keys)

前端 未结 10 867
清歌不尽
清歌不尽 2021-01-31 19:02

let\'s have this hash:

hash = {\"a\" => 1, \"b\" => {\"c\" => 3}}
hash.get_all_keys 
=> [\"a\", \"b\", \"c\"]

how can i get all key

相关标签:
10条回答
  • This will give you an array of all the keys for any level of nesting.

    def get_em(h)
      h.each_with_object([]) do |(k,v),keys|      
        keys << k
        keys.concat(get_em(v)) if v.is_a? Hash
      end
    end
    
    hash = {"a" => 1, "b" => {"c" => {"d" => 3}}}
    get_em(hash) #  => ["a", "b", "c", "d"]
    
    0 讨论(0)
  • Version that keeps the hierarchy of the keys

    • Works with arrays
    • Works with nested hashes

    keys_only.rb

    # one-liner
    def keys_only(h); h.map { |k, v| v = v.first if v.is_a?(Array); v.is_a?(Hash) ? [k, keys_only(v)] : k }; end
    
    # nicer
    def keys_only(h)
      h.map do |k, v|
        v = v.first if v.is_a?(Array);
    
        if v.is_a?(Hash)
          [k, keys_only(v)]
        else
          k
        end
      end
    end
    
    hash = { a: 1, b: { c: { d: 3 } }, e: [{ f: 3 }, { f: 5 }] }
    keys_only(hash)
    # => [:a, [:b, [[:c, [:d]]]], [:e, [:f]]]
    

    P.S.: Yes, it looks like a lexer :D

    Bonus: Print the keys in a nice nested list

    # one-liner
    def print_keys(a, n = 0); a.each { |el| el.is_a?(Array) ? el[1] && el[1].class == Array ? print_keys(el, n) : print_keys(el, n + 1) : (puts "  " * n + "- #{el}") }; nil; end
    
    # nicer
    def print_keys(a, n = 0)
      a.each do |el|
        if el.is_a?(Array)
           if el[1] && el[1].class == Array
             print_keys(el, n)
           else
             print_keys(el, n + 1)
           end
        else
          puts "  " * n + "- #{el}"
        end
      end
    
      nil
    end
    
    
    > print_keys(keys_only(hash))
    - a
      - b
          - c
            - d
      - e
        - f
    
    0 讨论(0)
  • 2021-01-31 19:28

    Also deal with nested arrays that include hashes

    def all_keys(items)
      case items
      when Hash then items.keys + items.values.flat_map { |v| all_keys(v) }
      when Array then items.flat_map { |i| all_keys(i) }
      else []
      end
    end
    
    0 讨论(0)
  • 2021-01-31 19:30

    Please take a look of following code:

    hash = {"a" => 1, "b" => {"c" => 3}}
    keys = hash.keys + hash.select{|_,value|value.is_a?(Hash)}
           .map{|_,value| value.keys}.flatten
    p keys
    

    result:

    ["a", "b", "c"]
    

    New solution, considering @Bala's comments.

    class Hash
      def recursive_keys
        if any?{|_,value| value.is_a?(Hash)}
           keys + select{|_,value|value.is_a?(Hash)}
                        .map{|_,value| value.recursive_keys}.flatten
        else
           keys
        end
      end
    end
    
    hash =  {"a" => 1, "b" => {"c" => {"d" => 3}}, "e" => {"f" => 3}}
    p hash.recursive_keys
    

    result:

    ["a", "b", "e", "c", "d", "f"]
    
    0 讨论(0)
  • 2021-01-31 19:31

    I'm sure there is a more elegant solution, but this option works:

    blah = {"a" => 1, "b" => {"c" => 3}}
    results = []
    blah.each do |k,v|
      if v.is_a? Hash
        results << k
        v.each_key {|key| results << key}
      else
        results << k
      end
    end
    puts results
    
    0 讨论(0)
  • 2021-01-31 19:36

    I find grep useful here:

    def get_keys(hash)
      ( hash.keys + hash.values.grep(Hash){|sub_hash| get_keys(sub_hash) } ).flatten
    end
    
    p get_keys my_nested_hash #=> ["a", "b", "c"]
    

    I like the solution as it is short, yet it reads very nicely.

    0 讨论(0)
提交回复
热议问题