Class: Hashery::PathHash
Overview
This class is very much a work in progess and will be substantially rewritten for future versions.
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 <[email protected]>.
Copyright © 2006 BehindLogic (hash_magic.rubyforge.org)
Instance Method Summary collapse
- #==(other) ⇒ Object
-
#[](key) ⇒ 'c/d'
Behaves like the usual Hash#[] method, but you can access nested hash values by composing a single key of the traversing keys joined by ‘/’:.
-
#[]=(key, value) ⇒ Object
Same as above, except sets value rather than retrieving it.
-
#clear ⇒ Object
:nodoc:.
-
#delete(key, &block) ⇒ Object
Delete entry from Hash.
- #empty? ⇒ Boolean
-
#expand ⇒ Object
Expands the whole hash to Hash objects …
- #fetch(key, default = :ehisehoah0928309q98y30, &block) ⇒ Object
-
#flat ⇒ 'a/b', ...
Gives a list of all keys in all levels in the multi-level hash, joined by slashes.
- #flatten_to_array(value, a) ⇒ Object private
- #flatten_to_hash(hsh) ⇒ Object private
-
#index(value) ⇒ Object
This gives you the slashed key of the value, no matter where the value is in the tree.
-
#initialize(hsh = {}) ⇒ PathHash
constructor
Initialize PathHash.
- #inspect ⇒ Object
-
#keys ⇒ Object
This gives you only the top-level keys, no slashes.
-
#ordered(*keys_in_order) ⇒ Object
Same as ordered! but returns a new SlashedHash object instead of modifying the same.
-
#ordered!(*keys_in_order) ⇒ Object
Sets the SlashedArray as ordered.
-
#rehash ⇒ Object
This is rewritten to mean something slightly different than usual: Use this to restructure the hash, for cases when you end up with an array holding several hashes.
-
#slashed ⇒ Object
:nodoc:.
- #to_string_array ⇒ Object
Methods inherited from Hash
create, #rekey, #rekey!, #retrieve, #to_hash, #to_stash
Constructor Details
#initialize(hsh = {}) ⇒ PathHash
Initialize PathHash.
31 32 33 34 35 |
# File 'lib/hashery/path_hash.rb', line 31 def initialize(hsh={}) raise ArgumentError, "must be a hash or array of slashed values" unless hsh.is_a?(Hash) || hsh.is_a?(Array) @constructor = hsh.is_a?(Hash) ? hsh.class : Hash @flat = flatten_to_hash(hsh) end |
Instance Method Details
#==(other) ⇒ Object
180 181 182 183 184 185 186 187 188 189 |
# 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 |
#[](key) ⇒ 'c/d'
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']
46 47 48 49 50 51 52 53 54 55 |
# 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 |
#[]=(key, value) ⇒ Object
Same as above, except sets value rather than retrieving it.
60 61 62 63 64 65 66 67 68 69 |
# 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 |
#clear ⇒ Object
:nodoc:
71 72 73 |
# File 'lib/hashery/path_hash.rb', line 71 def clear # :nodoc: @flat.clear end |
#delete(key, &block) ⇒ Object
Delete entry from Hash. Slashed keys can be used here, too.
104 105 106 107 108 109 |
# 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 |
#empty? ⇒ Boolean
112 113 114 |
# File 'lib/hashery/path_hash.rb', line 112 def empty? @flat.empty? end |
#expand ⇒ Object
Expands the whole hash to Hash objects … not useful very often, it seems.
147 148 149 |
# File 'lib/hashery/path_hash.rb', line 147 def inject({}) {|h,(k,v)| h[k] = v.is_a?(SlashedHash) ? v. : v; h} end |
#fetch(key, default = :ehisehoah0928309q98y30, &block) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/hashery/path_hash.rb', line 78 def fetch(key,default=:ehisehoah0928309q98y30,&block) # :nodoc: 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}) if value.is_a?(self.class) && value.empty? if default == :ehisehoah0928309q98y30 if block_given? block.call(key) else raise IndexError end value else default end else value end end |
#flat ⇒ 'a/b', ...
Gives a list of all keys in all levels in the multi-level hash, joined by slashes.
‘c’=>‘d’, ‘b’=>‘c’}.slashed.flat.keys #=> [‘a/b’, ‘a/c’, ‘b’]
142 143 144 |
# File 'lib/hashery/path_hash.rb', line 142 def flat @flat end |
#flatten_to_array(value, a) ⇒ Object (private)
226 227 228 229 230 231 232 233 234 235 |
# 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 |
#flatten_to_hash(hsh) ⇒ Object (private)
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# 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 |
#index(value) ⇒ Object
This gives you the slashed key of the value, no matter where the value is in the tree.
117 118 119 |
# File 'lib/hashery/path_hash.rb', line 117 def index(value) @flat.index(value) end |
#inspect ⇒ Object
122 123 124 |
# File 'lib/hashery/path_hash.rb', line 122 def inspect @flat.inspect.insert(1,'slashed: ') end |
#keys ⇒ Object
This gives you only the top-level keys, no slashes. To get the list of slashed keys, do hash.flat.keys
127 128 129 |
# File 'lib/hashery/path_hash.rb', line 127 def keys @flat.inject([]) {|a,(k,v)| a << [k.split('/',2)].flatten[0]; a}.uniq end |
#ordered(*keys_in_order) ⇒ Object
Same as ordered! but returns a new SlashedHash object instead of modifying the same.
160 161 162 |
# File 'lib/hashery/path_hash.rb', line 160 def ordered(*keys_in_order) dup.ordered!(*keys_in_order) end |
#ordered!(*keys_in_order) ⇒ Object
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. # => {'b'=>'c', 'a'=>{'c'=>'d', 'b'=>'c'}}
# Note that the expanded hashes will *still* be ordered!
172 173 174 175 176 177 |
# 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 |
#rehash ⇒ Object
This is rewritten to mean something slightly different than usual: Use this to restructure the hash, for cases when you end up with an array holding several hashes.
133 134 135 |
# File 'lib/hashery/path_hash.rb', line 133 def rehash # :nodoc: @flat.rehash end |
#slashed ⇒ Object
:nodoc:
155 156 157 |
# File 'lib/hashery/path_hash.rb', line 155 def slashed # :nodoc: self end |
#to_string_array ⇒ Object
151 152 153 |
# File 'lib/hashery/path_hash.rb', line 151 def to_string_array flatten_to_array(flat,[]) end |