Class: Hash

Inherits:
Object show all
Defined in:
lib/epitools/core_ext/hash.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.lazy!Object

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.



133
134
135
136
137
138
139
140
141
142
143
# File 'lib/epitools/core_ext/hash.rb', line 133

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

.of_arraysObject

Returns a new Hash whose values default to empty arrays. (Good for collecting things!)

eg:

Hash.of_arrays[:yays] << "YAY!"


88
89
90
# File 'lib/epitools/core_ext/hash.rb', line 88

def self.of_arrays
  new {|h,k| h[k] = [] }
end

.of_integersObject

Returns a new Hash whose values default to 0. (Good for counting things!)

eg:

Hash.of_integers[:yays] += 1


108
109
110
# File 'lib/epitools/core_ext/hash.rb', line 108

def self.of_integers
  new(0)
end

.of_setsObject

Returns a new Hash whose values default to empty sets. (Good for collecting unique things!)

eg:

Hash.of_sets[:yays] << "Yay!"


98
99
100
# File 'lib/epitools/core_ext/hash.rb', line 98

def self.of_sets
  new {|h,k| h[k] = Set.new }
end

.of_unique_idsObject

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}


125
126
127
# File 'lib/epitools/core_ext/hash.rb', line 125

def self.of_unique_ids
  new { |h,k| h[k] = h.size }
end

Instance Method Details

#apply_diff(changes) ⇒ Object

Applies a Hash#diff changeset and returns the transformed hash.



296
297
298
# File 'lib/epitools/core_ext/hash.rb', line 296

def apply_diff(changes)
  deep_dup.apply_diff!(changes)
end

#apply_diff!(changes) ⇒ Object

Applies a Hash#diff changeset to this hash.



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/epitools/core_ext/hash.rb', line 270

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

#blank?Boolean

‘true’ if the Hash has no entries

Returns:

  • (Boolean)


7
8
9
# File 'lib/epitools/core_ext/hash.rb', line 7

def blank?
  not any?
end

#deep_dupObject

Duplicate this hash, including hashes nested inside of it.



303
304
305
306
307
308
309
310
# File 'lib/epitools/core_ext/hash.rb', line 303

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

#diff(other) ⇒ Object

Return all the changes necessary to transform ‘self` into `other`. (Works on nested hashes.) The result is a hash of => [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”.)



254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/epitools/core_ext/hash.rb', line 254

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

#map_keys(&block) ⇒ Object

Transforms the keys of the hash by passing them into the supplied block, and then using the blocks result as the new key.



61
62
63
# File 'lib/epitools/core_ext/hash.rb', line 61

def map_keys(&block)
  dup.map_keys!(&block)
end

#map_keys!(&block) ⇒ Object

Runs map_keys on self.



49
50
51
52
53
54
55
# File 'lib/epitools/core_ext/hash.rb', line 49

def map_keys!(&block)
  keys.each do |key|
    value = delete(key)
    self[yield(key)] = value
  end
  self
end

#map_values(&block) ⇒ Object

Transforms the values of the hash by passing them into the supplied block, and then using the block’s result as the new value.



42
43
44
# File 'lib/epitools/core_ext/hash.rb', line 42

def map_values(&block)
  dup.map_values!(&block)
end

#map_values!(&block) ⇒ Object

Runs map_values on self.



30
31
32
33
34
35
36
# File 'lib/epitools/core_ext/hash.rb', line 30

def map_values!(&block)
  keys.each do |key|
    value = self[key]
    self[key] = yield(value)
  end
  self
end

#mkdir_p(path) ⇒ Object

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"=>{}}}}


160
161
162
163
164
165
166
# File 'lib/epitools/core_ext/hash.rb', line 160

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`



184
185
186
187
188
189
190
191
# File 'lib/epitools/core_ext/hash.rb', line 184

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(template) ⇒ Object Also known as: mql

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")


232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/epitools/core_ext/hash.rb', line 232

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

#remove_blank_valuesObject

Returns a new Hash where blank values have been removed. (It checks if the value is blank by calling #blank? on it)



23
24
25
# File 'lib/epitools/core_ext/hash.rb', line 23

def remove_blank_values
  dup.remove_blank_values!
end

#remove_blank_values!Object

Runs “remove_blank_values” on self.



14
15
16
17
# File 'lib/epitools/core_ext/hash.rb', line 14

def remove_blank_values!
  delete_if{|k,v| v.blank?}
  self
end

#slice(*keys) ⇒ Object

Returns a hash containing only the keys passed as arguments.



68
69
70
# File 'lib/epitools/core_ext/hash.rb', line 68

def slice(*keys)
  dup.slice!(*keys)
end

#slice!(*keys) ⇒ Object

Alters the hash so it contains only the keys passed as arguments.



76
77
78
79
80
# File 'lib/epitools/core_ext/hash.rb', line 76

def slice!(*keys)
  keys = Set.new keys
  delete_if { |k,v| not keys.include? k }
  self
end

#to_queryObject

Convert the hash into a GET query.



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
# File 'lib/epitools/core_ext/hash.rb', line 196

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

#tree(level = 0, indent = " ") ⇒ Object

Turn some nested hashes into a tree (returns an array of strings, padded on the left with indents.)



171
172
173
174
175
176
177
178
179
# File 'lib/epitools/core_ext/hash.rb', line 171

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