module Hm::Algo

@private

Constants

NONDUPABLE

JRuby, I am looking at you

Public Instance Methods

deep_copy(value) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source

Enumerates through entire collection with “current key/current value” at each point, even if elements are deleted in a process of enumeration

# 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: ->(*) {} click to toggle source
# 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 = []) { |what, [*path, key], val| ... } click to toggle source
# 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:) click to toggle source
# 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:) click to toggle source
# 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