class Hash def extract_keys keys = [] each_pair do |k1, v1| if v1.is_a?(Hash) v1.each_pair { |k2, v2| if !v2.is_a?(Hash) then keys << [k1, k2]; next end v2.each_pair { |k3, v3| if !v3.is_a?(Hash) then keys << [k1, k2, k3]; next end v3.each_pair { |k4, v4| if !v4.is_a?(Hash) then keys << [k1, k2, k3, k4]; next end v4.each_pair { |k5, v5| if !v5.is_a?(Hash) then keys << [k1, k2, k3, k4, k5]; next end v5.each_pair { |k6, v6| if !v6.is_a?(Hash) then keys << [k1, k2, k3, k4, k5, k6]; next end # add more v[n].each_pair ... loops to process more hash dimensions } } } } } # "}" * 5 else keys << [k1] end end keys end def all_values extract_keys.map do |subar| key = "" subar.size.times { |i| key << "[subar[#{i}]]" } hash_str = "self" << key << " rescue nil" # example: "self[subar[0]][subar[1]][subar[2]][subar[3]] rescue nil" hash_value = eval(hash_str) end end #------------------------- # Find every path and it's value in a Hash, http://snippets.dzone.com/posts/show/3565 # Author: Florian Aßmann def each_path raise ArgumentError unless block_given? self.class.each_path(self) { |path, object| yield(path, object) } end protected #def self.each_path(object, path = '', &block) def self.each_path(object, path = [], &block) # alternative if object.is_a?(Hash) object.each do |key, value| #self.each_path(value, "#{ path }#{ key }/", &block) self.each_path(value, [path , key].flatten, &block) # alternative end else yield(path, object) end end end h = {"a"=>"b", "c"=>"d", 1=>{2=>{"e"=>"f", 3=>{4=>"value"}}}} puts h[1][2].class # Hash puts h[1][2]["e"].class # String extracted_keys = h.extract_keys puts extracted_keys.inspect # [["a"], [1, 2, "e"], [1, 2, 3, 4], ["c"]] puts h[1][2].has_key?("e") # true puts extracted_keys.include?([1, 2, "e"]) # true h = {700=>{4=>"value"}, "a"=>"b", 3=>{4=>"value"}, "c"=>"d", 1=>{2=>{"e"=>"f", 3=>{4=>"value"}, 300=>{4=>"value"}}}} p h p h.extract_keys #=> [["a"], [1, 2, "e"], [1, 2, 300, 4], [1, 2, 3, 4], ["c"], [700, 4], [3, 4]] p h.all_values #=> ["b", "f", "value", "value", "d", "value", "value"] #----------------- paths = [] complex_hash = Hash[ :a => { :aa => '1', :ab => '2' }, :b => { :ba => '3', :bb => '4' } ] complex_hash.each_path { |path, value| paths << [ path, value ] } p paths # => [[[:b, :ba], "3"], [[:b, :bb], "4"], [[:a, :ab], "2"], [[:a, :aa], "1"]] puts h = {"a"=>"b", "c"=>"d", 1=>{2=>{"e"=>"f", 3=>{4=>"value"}}}} h = {"a"=>"b", "l" => lambda { |x| x+1 }, 1=>{2=>{"e"=>"f", 3=>{4=>"value"}}}} h = {700=>{4=>"value"}, "a"=>"b", nil => "NILVALUE", 3=>{4=>"value"}, "c"=>"d", 1=>{2=>{"e"=>"f", 3=>{4=>"value"}, 300=>{4=>"value"}}}} p h p h.extract_keys keys = [] h.each_path { |path, value| keys << path } p keys # complete key sequences (with last key not pointing to another hash) paths = [] h.each_path { |path, value| paths << [ path, value ] } p paths # complete key sequences plus values vals = [] h.each_path { |path, value| vals << value } p vals # all values of complete key sequences p h.all_values # same
My goal was to extract the complete key sequences so that it becomes possible to handle nested values or
check if there is a given key sequence in a multi-dimensional hash (cf. "No keys, nor other hash methods on
multidimensional hash" via http://www.rubyweeklynews.org/20050828 ).
class Hash def extract_keys(keys = [], temp = []) temp = temp.dup temp2 = temp.dup self.each { |key, value| if value.class != Hash temp << key keys << temp #puts temp.inspect temp = [] end } temp = temp2 self.each { |key, value| if value.class == Hash temp << key keys = value.extract_keys(keys, temp) end } keys end end
You need to create an account or log in to post comments to this site.
[[[1, 2, 3]], [1, 2, "e"], [1, 2, 3, 4], [[1, 2, 3]]]
..
Do you want to do this instead?
h = {"a"=>"b", "c"=>"d", 1=>{2=>{"e"=>"f", 3=>{4=>"value"}}}}