class Hash
Public Class Methods
Hash
keys become methods, kinda like OpenStruct
. These methods have the lowest priority, so be careful. They will be overridden by any methods on Hash
.
# File lib/epitools/core_ext/hash.rb, line 190 def self.lazy! Hash.class_eval do def method_missing(name, *args) if args.any? super else self[name] || self[name.to_s] end end end end
Returns a new Hash
whose values default to empty arrays. (Good for collecting things!)
eg:
Hash.of_arrays[:yays] << "YAY!"
# File lib/epitools/core_ext/hash.rb, line 145 def self.of_arrays new {|h,k| h[k] = [] } end
Returns a new Hash
whose values default to 0. (Good for counting things!)
eg:
Hash.of_integers[:yays] += 1
# File lib/epitools/core_ext/hash.rb, line 165 def self.of_integers new(0) end
Returns a new Hash
whose values default to empty sets. (Good for collecting unique things!)
eg:
Hash.of_sets[:yays] << "Yay!"
# File lib/epitools/core_ext/hash.rb, line 155 def self.of_sets new {|h,k| h[k] = Set.new } end
Returns a new Hash
which automatically assigns each unique key to an increasing counter.
eg:
> h = Hash.of_unique_ids => {} > h["Person"] #=> 0 > h["Another Person"] #=> 1 > h["Yet Another Person"] #=> 2 > h["Person"] #=> 0 > h => {"Person"=>0, "Another Person"=>1, "Yet Another Person"=>2}
# File lib/epitools/core_ext/hash.rb, line 182 def self.of_unique_ids new { |h,k| h[k] = h.size } end
Public Instance Methods
‘hash1 + hash2` merge the two hashes, returning a new hash
# File lib/epitools/core_ext/hash.rb, line 7 def +(other) merge(other) end
‘hash1 - hash2` removes keys from hash1 that exist in hash2, returning a new hash
# File lib/epitools/core_ext/hash.rb, line 14 def -(other) dup.delete_if { |k,v| other.includes?(k) } end
Applies a Hash#diff
changeset and returns the transformed hash.
# File lib/epitools/core_ext/hash.rb, line 353 def apply_diff(changes) deep_dup.apply_diff!(changes) end
Applies a Hash#diff
changeset to this hash.
# File lib/epitools/core_ext/hash.rb, line 327 def apply_diff!(changes) path = [[self, changes]] pos, local_changes = path.pop while local_changes local_changes.each_pair do |key, change| if change.kind_of?(Array) if change[1].nil? pos.delete key else pos[key] = change[1] end else path.push([pos[key], change]) end end pos, local_changes = path.pop end self end
‘true’ if the Hash
has no entries
# File lib/epitools/core_ext/hash.rb, line 21 def blank? not any? end
Duplicate this hash, including hashes nested inside of it.
# File lib/epitools/core_ext/hash.rb, line 360 def deep_dup duplicate = self.dup duplicate.each_pair do |k,v| tv = duplicate[k] duplicate[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_dup : v end duplicate end
Return all the changes necessary to transform ‘self` into `other`. (Works on nested hashes.) The result is a hash of {:key => [old value, new value]} pairs.
(NOTE: Since “nil” is used to denote a value was removed, you can’t use this method to diff hashes where a value is “nil”.)
# File lib/epitools/core_ext/hash.rb, line 311 def diff(other) (self.keys + other.keys).uniq.inject({}) do |memo, key| unless self[key] == other[key] if self[key].kind_of?(Hash) && other[key].kind_of?(Hash) memo[key] = self[key].diff(other[key]) else memo[key] = [self[key], other[key]] end end memo end end
Transforms the keys of the hash by passing them into the supplied block, and then using the blocks result as the new key.
# File lib/epitools/core_ext/hash.rb, line 76 def map_keys(&block) dup.map_keys!(&block) end
Runs map_keys
on self.
# File lib/epitools/core_ext/hash.rb, line 63 def map_keys!(&block) keys.each do |key| value = delete(key) self[yield(key)] = value end self end
Transforms the values of the hash by passing them into the supplied block, and then using the block’s result as the new value.
# File lib/epitools/core_ext/hash.rb, line 56 def map_values(&block) dup.map_values!(&block) end
Runs map_values
on self.
# File lib/epitools/core_ext/hash.rb, line 44 def map_values!(&block) keys.each do |key| value = self[key] self[key] = yield(value) end self end
# File lib/epitools/core_ext/hash.rb, line 192 def method_missing(name, *args) if args.any? super else self[name] || self[name.to_s] end end
Makes each element in the ‘path` array point to a hash containing the next element in the `path`. Useful for turning a bunch of strings (paths, module names, etc.) into a tree.
Example:
h = {} h.mkdir_p(["a", "b", "c"]) #=> {"a"=>{"b"=>{"c"=>{}}}} h.mkdir_p(["a", "b", "whoa"]) #=> {"a"=>{"b"=>{"c"=>{}, "whoa"=>{}}}}
# File lib/epitools/core_ext/hash.rb, line 217 def mkdir_p(path) return if path.empty? dir = path.first self[dir] ||= {} self[dir].mkdir_p(path[1..-1]) self end
Print the result of ‘tree`
# File lib/epitools/core_ext/hash.rb, line 241 def print_tree(level=0, indent=" ", &block) dent = indent * level each do |key, val| puts block_given? ? yield(key, level) : "#{dent}#{key}" val.print_tree(level+1, indent, &block) if val.any? end end
Query a hash using MQL (see: wiki.freebase.com/wiki/MQL_operators for reference)
Examples:
> query(name: /steve/) > query(/title/ => ??) > query(articles: [{title: ??}]) > query(responses: []) > query("date_of_birth<" => "2000")
# File lib/epitools/core_ext/hash.rb, line 289 def query(template) results = [] template.each do |key,val| case key when Regexp, String when Array when Hash results += hash.query(template) end end map do |key,val| end end
Runs “remove_blank_values” on self.
# File lib/epitools/core_ext/hash.rb, line 28 def remove_blank_values! delete_if{|k,v| v.blank?} self end
Returns a hash containing only the keys passed as arguments.
# File lib/epitools/core_ext/hash.rb, line 125 def slice(*keys) dup.slice!(*keys) end
Alters the hash so it contains only the keys passed as arguments.
# File lib/epitools/core_ext/hash.rb, line 133 def slice!(*keys) keys = Set.new keys delete_if { |k,v| not keys.include? k } self end
Return a hash with its keys converted to symbols, for great justice
# File lib/epitools/core_ext/hash.rb, line 118 def symbolize_keys dup.symbolize_keys! end
Convert the keys to symbols in-place, for fun and profit
# File lib/epitools/core_ext/hash.rb, line 111 def symbolize_keys! map_keys! { |k| k.to_sym } end
Convert this Hash
to indented JSON (using JSON.pretty_generate)
# File lib/epitools/core_ext/hash.rb, line 381 def to_nicejson JSON.pretty_generate(self) end
Convert this Hash
(and all nested Hashes, and all nested Arrays containing Hashes) to OpenStruct(s)
# File lib/epitools/core_ext/hash.rb, line 372 def to_ostruct OpenStruct.new(self.map_values do |v| v.respond_to?(:to_ostruct) ? v.to_ostruct : v end) end
Convert the hash into a GET query.
# File lib/epitools/core_ext/hash.rb, line 253 def to_query params = '' stack = [] each do |k, v| if v.is_a?(Hash) stack << [k,v] else params << "#{k}=#{v}&" end end stack.each do |parent, hash| hash.each do |k, v| if v.is_a?(Hash) stack << ["#{parent}[#{k}]", v] else params << "#{parent}[#{k}]=#{v}&" end end end params.chop! # trailing & params end
Same as ‘translate_keys!`, except it returns a copy of the hash
# File lib/epitools/core_ext/hash.rb, line 104 def translate_keys(mapping) dup.translate_keys!(mapping) end
Translate keys to other keys, given a hash of old-key to new-key mappings.
eg: hash.translate_keys!(
"Extreme Sports!!!!" => :extreme_sports, "Mediocre sports" => :sports, "Pretty okay sports" => :sports, "Golf" => :liesure_activities, )
# File lib/epitools/core_ext/hash.rb, line 91 def translate_keys!(mapping) # TODO: Allow regexes and lambdas (eg: translate_keys!(/.+/ => ->(key) { key.to_sym }) mapping.each do |src,dest| if includes? src self[dest] = delete(src) end end self end
Turn some nested hashes into a tree (returns an array of strings, padded on the left with indents.)
# File lib/epitools/core_ext/hash.rb, line 228 def tree(level=0, indent=" ") result = [] dent = indent * level each do |key, val| result << dent+key.to_s result += val.tree(level+1) if val.any? end result end