Module: Hm::Algo
- Defined in:
- lib/hm/algo.rb
Constant Summary collapse
- NONDUPABLE =
JRuby, I am looking at you
[Symbol, Numeric, NilClass, TrueClass, FalseClass].freeze
Class Method Summary collapse
- .deep_copy(value) ⇒ Object
- .delete(collection, key) ⇒ Object
- .nest_hashes(value, *keys) ⇒ Object
-
.robust_enumerator(collection) ⇒ Object
Enumerates through entire collection with “current key/current value” at each point, even if elements are deleted in a process of enumeration.
- .visit(what, rest, path = [], not_found: ->(*) {}, &found) ⇒ Object
- .visit_all(what, path = [], &block) ⇒ Object
-
.visit_regular(what, key, rest, path, found:, not_found:) ⇒ Object
rubocop:disable Metrics/ParameterLists.
- .visit_wildcard(what, rest, path, found:, not_found:) ⇒ Object
Class Method Details
.deep_copy(value) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/hm/algo.rb', line 13 def deep_copy(value) # FIXME: ignores Struct/OpenStruct (which are diggable too) case value when Hash value.map { |key, val| [key, deep_copy(val)] }.to_h when Array value.map(&method(:deep_copy)) when *NONDUPABLE value else value.dup end end |
.delete(collection, key) ⇒ Object
6 7 8 |
# File 'lib/hm/algo.rb', line 6 def delete(collection, key) collection.is_a?(Array) ? collection.delete_at(key) : collection.delete(key) end |
.nest_hashes(value, *keys) ⇒ Object
49 50 51 52 53 54 55 |
# File 'lib/hm/algo.rb', line 49 def nest_hashes(value, *keys) return value if keys.empty? key = keys.shift val = keys.empty? ? value : nest_hashes(value, *keys) key.is_a?(Integer) ? [].tap { |arr| arr[key] = val } : {key => val} end |
.robust_enumerator(collection) ⇒ Object
Enumerates through entire collection with “current key/current value” at each point, even if elements are deleted in a process of enumeration
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/hm/algo.rb', line 29 def robust_enumerator(collection) case collection when Hash, Struct collection.each_pair.to_a when Array Enumerator.new do |y| cur = collection.size until cur.zero? pos = collection.size - cur y << [pos, collection[pos]] cur -= 1 end end when ->(c) { c.respond_to?(:each_pair) } collection.each_pair.to_a else fail TypeError, "Can't dig/* in #{collection.class}" end end |
.visit(what, rest, path = [], not_found: ->(*) {}, &found) ⇒ Object
57 58 59 60 61 62 63 64 65 66 |
# File 'lib/hm/algo.rb', line 57 def visit(what, rest, path = [], not_found: ->(*) {}, &found) Dig.diggable?(what) or fail TypeError, "#{what.class} is not diggable" key, *rst = rest if key == WILDCARD visit_wildcard(what, rst, path, found: found, not_found: not_found) else visit_regular(what, key, rst, path, found: found, not_found: not_found) end end |
.visit_all(what, path = [], &block) ⇒ Object
68 69 70 71 72 73 |
# File 'lib/hm/algo.rb', line 68 def visit_all(what, path = [], &block) robust_enumerator(what).each do |key, val| yield(what, [*path, key], val) visit_all(val, [*path, key], &block) if Dig.diggable?(val) end end |
.visit_regular(what, key, rest, path, found:, not_found:) ⇒ Object
rubocop:disable Metrics/ParameterLists
84 85 86 87 88 89 90 91 92 |
# File 'lib/hm/algo.rb', line 84 def visit_regular(what, key, rest, path, found:, not_found:) # rubocop:disable Metrics/ParameterLists internal = Dig.dig(what, key) # NB: NotFound is signified by special value, because `nil` can still be legitimate value in hash return not_found.(what, [*path, key], rest) if internal == Dig::NotFound rest.empty? and return found.(what, [*path, key], internal) visit(internal, rest, [*path, key], not_found: not_found, &found) end |
.visit_wildcard(what, rest, path, found:, not_found:) ⇒ Object
75 76 77 78 79 80 81 82 |
# File 'lib/hm/algo.rb', line 75 def visit_wildcard(what, rest, path, found:, not_found:) iterator = robust_enumerator(what) if rest.empty? iterator.map { |key, val| found.(what, [*path, key], val) } else iterator.map { |key, el| visit(el, rest, [*path, key], not_found: not_found, &found) } end end |