class Hashery::PathHash
A PathHash
is a hash whose values can be accessed in the normal manner, or with keys that are slash (‘/`) separated strings. To get the whole hash as a single flattened level, call `#flat`. All keys are converted to strings. All end-of-the-chain values are kept in whatever value they are.
s = PathHash['a' => 'b', 'c' => {'d' => :e}] s['a'] #=> 'b' s['c'] #=> {slashed: 'd'=>:e} s['c']['d'] #=> :e s['c/d'] #=> :e
PathHash
is derived from the SlashedHash class in the HashMagic project by Daniel Parker <gems@behindlogic.com>.
Copyright © 2006 BehindLogic (hash_magic.rubyforge.org)
Authors: Daniel Parker
TODO: This class is very much a work in progess and will be substantially rewritten for future versions.
Public Class Methods
Public Instance Methods
# File lib/hashery/path_hash.rb, line 180 def ==(other) case other when SlashedHash @slashed == other.instance_variable_get(:@slashed) when Hash self == SlashedHash.new(other) else raise TypeError, "Cannot compare #{other.class.name} with SlashedHash" end end
Behaves like the usual Hash#[] method, but you can access nested hash values by composing a single key of the traversing keys joined by ‘/’:
hash['c']['d'] # is the same as: hash['c/d']
# File lib/hashery/path_hash.rb, line 46 def [](key) rg = Regexp.new("^#{key}/?") start_obj = if @constructor == OrderedHash @constructor.new((@flat.instance_variable_get(:@keys_in_order) || []).collect {|e| e.gsub(rg,'')}) else @constructor.new end v = @flat.has_key?(key) ? @flat[key] : self.class.new(@flat.reject {|k,v| !(k == key || k =~ rg)}.inject(start_obj) {|h,(k,v)| h[k.gsub(rg,'')] = v; h}) v.is_a?(self.class) && v.empty? ? nil : v end
Same as above, except sets value rather than retrieving it.
# File lib/hashery/path_hash.rb, line 60 def []=(key,value) @flat.reject! {|k,v| k == key || k =~ Regexp.new("^#{key}/")} if value.is_a?(Hash) flatten_to_hash(value).each do |hk,hv| @flat[key.to_s+'/'+hk.to_s] = hv end else @flat[key.to_s] = value end end
Delete entry from Hash
. Slashed keys can be used here, too.
key - The key to delete. block - Produces the return value if key not found.
Returns delete value.
# File lib/hashery/path_hash.rb, line 104 def delete(key,&block) value = @flat.has_key?(key) ? @flat[key] : self.class.new(@flat.reject {|k,v| !(k == key || k =~ Regexp.new("^#{key}/"))}.inject({}) {|h,(k,v)| h[k.split('/',2)[1]] = v; h}) return block.call(key) if value.is_a?(self.class) && value.empty? && block_given? @flat.keys.reject {|k| !(k == key || k =~ Regexp.new("^#{key}/"))}.each {|k| @flat.delete(k)} return value end
# File lib/hashery/path_hash.rb, line 112 def empty? @flat.empty? end
Expands the whole hash to Hash
objects … not useful very often, it seems.
# File lib/hashery/path_hash.rb, line 147 def expand inject({}) {|h,(k,v)| h[k] = v.is_a?(SlashedHash) ? v.expand : v; h} end
Gives a list of all keys in all levels in the multi-level hash, joined by slashes.
{'a'=>{'b'=>'c', 'c'=>'d'}, 'b'=>'c'}.slashed.flat.keys #=> ['a/b', 'a/c', 'b']
# File lib/hashery/path_hash.rb, line 142 def flat @flat end
This gives you the slashed key of the value, no matter where the value is in the tree.
# File lib/hashery/path_hash.rb, line 117 def index(value) @flat.index(value) end
# File lib/hashery/path_hash.rb, line 122 def inspect @flat.inspect.insert(1,'slashed: ') end
This gives you only the top-level keys, no slashes. To get the list of slashed keys, do hash.flat.keys
# File lib/hashery/path_hash.rb, line 127 def keys @flat.inject([]) {|a,(k,v)| a << [k.split('/',2)].flatten[0]; a}.uniq end
Same as ordered! but returns a new SlashedHash object instead of modifying the same.
# File lib/hashery/path_hash.rb, line 160 def ordered(*keys_in_order) dup.ordered!(*keys_in_order) end
Sets the SlashedArray as ordered. The *keys_in_order must be a flat array of slashed keys that specify the order for each level:
s = {'a'=>{'b'=>'c', 'c'=>'d'}, 'b'=>'c'}.slashed s.ordered!('b', 'a/c', 'a/b') s.expand # => {'b'=>'c', 'a'=>{'c'=>'d', 'b'=>'c'}} # Note that the expanded hashes will *still* be ordered!
# File lib/hashery/path_hash.rb, line 172 def ordered!(*keys_in_order) return self if @constructor == OrderedHash @constructor = OrderedHash @flat = @flat.ordered(*keys_in_order) self end
# File lib/hashery/path_hash.rb, line 151 def to_string_array flatten_to_array(flat,[]) end
Private Instance Methods
# File lib/hashery/path_hash.rb, line 226 def flatten_to_array(value,a) if value.is_a?(Array) value.each {|e| flatten_to_array(e,a)} elsif value.is_a?(Hash) value.inject([]) {|aa,(k,v)| flatten_to_array(v,[]).each {|vv| aa << k+'/'+vv.to_s}; aa}.each {|e| a << e} else a << value.to_s end a end
# File lib/hashery/path_hash.rb, line 193 def flatten_to_hash(hsh) flat = @constructor.new if hsh.is_a?(Array) hsh.each do |e| flat.merge!(flatten_to_hash(e)) end elsif hsh.is_a?(Hash) hsh.each do |k,v| if v.is_a?(Hash) flatten_to_hash(v).each do |hk,hv| flat[k.to_s+'/'+hk.to_s] = hv end else flat[k.to_s] = v end end else ks = hsh.split('/',-1) v = ks.pop ks = ks.join('/') if !flat[ks].nil? if flat[ks].is_a?(Array) flat[ks] << v else flat[ks] = [flat[ks], v] end else flat[ks] = v end end flat end