Module: HashOp::Deep

Defined in:
lib/hash_op/deep.rb

Class Method Summary collapse

Class Method Details

.build_with_segments(segments, value) ⇒ Object



126
127
128
129
# File 'lib/hash_op/deep.rb', line 126

def build_with_segments(segments, value)
  return value if segments.empty?
  { segments.first => build_with_segments(segments[1..-1], value) }
end

.fetch(hash, path) ⇒ Object

Examples:

h = {a: {b: {c: 1}}}
HashOp::Deep.fetch(h, :a) # => {:b=>{:c=>1}}
HashOp::Deep.fetch(h, :'a.b') # => {:c=>1}
HashOp::Deep.fetch(h, :'a.b.c') # => 1
HashOp::Deep.fetch(h, [:a]) # => {:b=>{:c=>1}}
HashOp::Deep.fetch(h, [:a, :b, :c]) # => 1
HashOp::Deep.fetch(h, :'b.c.a') # => nil


19
20
21
22
23
24
25
26
27
28
# File 'lib/hash_op/deep.rb', line 19

def fetch(hash, path)
  fail ArgumentError, "First argument must be an Hash (was #{hash})" unless hash.is_a?(Hash)
  if path.class.in? [String, Symbol]
    fetch_with_deep_key(hash, path)
  elsif path.is_a? Array
    fetch_with_segments(hash, path)
  else
    fail ArgumentError, 'Invalid attribute, must be a String or an Array'
  end
end

.fetch_with_deep_key(hash, deep_key) ⇒ Object



71
72
73
74
75
# File 'lib/hash_op/deep.rb', line 71

def fetch_with_deep_key(hash, deep_key)
  segments = deep_key.to_s.split('.')
  segments.map!(&:to_sym) if deep_key.is_a? Symbol
  fetch_with_segments(hash, segments)
end

.fetch_with_segments(hash, segments) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/hash_op/deep.rb', line 78

def fetch_with_segments(hash, segments)
  return hash if segments.empty?

  result = hash[segments.first]
  if result.is_a? Hash
    fetch_with_segments(result, segments[1..-1])
  elsif result.is_a? Array
    result.map do |item|
      fetch_with_segments(item, segments[1..-1])
    end
  else
    result
  end
end

.merge(hash, path, value) ⇒ Object

Examples:

h = {}
h = HashOp::Deep.merge(h, :a, 1)
=> { :a => 1 }
h = HashOp::Deep.merge(h, :'a.b', 2)
=> { :a => { :b => 2 }
h = HashOp::Deep.merge(h, :b, 3)
=> { :a => { :b => 2 }, :b => 3 }


39
40
41
42
43
44
45
46
47
48
# File 'lib/hash_op/deep.rb', line 39

def merge(hash, path, value)
  fail ArgumentError, 'First argument must be an Hash' unless hash.is_a? Hash
  if path.class.in? [String, Symbol]
    merge_with_deep_key(hash, path, value)
  elsif path.is_a? Array
    merge_with_segments(hash, path, value)
  else
    raise 'Invalid attribute, must be a String or an Array'
  end
end

.merge_with_deep_key(hash, deep_key, value) ⇒ Object



94
95
96
97
98
# File 'lib/hash_op/deep.rb', line 94

def merge_with_deep_key(hash, deep_key, value)
  segments = deep_key.to_s.split('.')
  segments.map!(&:to_sym) if deep_key.is_a? Symbol
  merge_with_segments(hash, segments, value)
end

.merge_with_segments(hash, segments, value) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/hash_op/deep.rb', line 101

def merge_with_segments(hash, segments, value)
  current_segment = segments.first
  remaining_segments = segments[1..-1]

  return value if segments.empty?

  current_value = hash[current_segment]
  new_value = (
    if remaining_segments.length > 0
      if current_value.is_a? Hash
        merge_with_segments(current_value, remaining_segments, value)
      elsif current_value.is_a? Array
        current_value.map do |item|
          merge_with_segments(item, remaining_segments, value)
        end
      else
        build_with_segments remaining_segments, value
      end
    else value
    end
  )
  hash.merge current_segment => new_value
end

.paths(hash) ⇒ Object

Example:

hash = { a: { :b => 1, 'c' => 2 }, d: 0 }
HashOp::Deep.paths(hash)
=> [[:a, :b], [:a, 'c' ], [:d]]


55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/hash_op/deep.rb', line 55

def paths(hash)
  r = []
  hash.each do |key, value|
    if value.is_a?(Hash)
      paths(value).each do |deep_key|
        r << [key] + Array(deep_key)
      end
    else r << Array(key)
    end
  end
  r.uniq
end