hash_miner 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47cc3f1132fb53760a709194961033a327634cda3d5b5f32583cb8ab8af1cc61
4
- data.tar.gz: 192a1594d31a7565aaa0d1bfc0835a29b2cdf57326c131bd6756aa58901fc5d8
3
+ metadata.gz: 914562162950918230de98d58303185f43568f78dfca068bc2f7209f4f96ae2b
4
+ data.tar.gz: 80e88d5b6d61b41e7aafb12fddf796c9eba40667895fc0cb8e62610e0160e04b
5
5
  SHA512:
6
- metadata.gz: 2dde32d4292f9f66374cac7c048ec5373537bfc4b988a563202aa824a394a6b7e3b0b6af057eda8e9c481aac93910b9a188714e6e2e77760c57debdd1fd98335
7
- data.tar.gz: 05354d5f27da7d4d0a6f0de3c58315929193596f9a08d651e83f352df6b9f3573070016bb8e84fb64eaa2ebf8395e97c4689fbf69d38476044ed991b17297233
6
+ metadata.gz: 52d4ae8b09bc42cccb5b3fd98180a100074292a98d4b0a0437d62f776e436b69b93c487f15524323995452c1a96b9a214e071bb9735dddfcfff0da251c6b2ed1
7
+ data.tar.gz: c5af637056ca8c4950846fc497ecac8f34270425f176a2850da0002cebeddbc09c71ed601d381f4e5feb5012bfad84d27db116ce8ab11a3b9413da4e335291f9
data/.rubocop.yml CHANGED
@@ -21,7 +21,7 @@ Metrics/BlockLength:
21
21
  Enabled: false
22
22
 
23
23
  Metrics/ClassLength:
24
- Max: 120
24
+ Max: 250
25
25
 
26
26
  Metrics/MethodLength:
27
27
  Max: 40
data/README.md CHANGED
@@ -64,6 +64,9 @@ nasty_hash.deep_update(key: :pretty, value: :super_duper, error_on_uniqueness: f
64
64
  nasty_hash.deep_update(key: :huh, value: :where_am_i) # => throws KeyNotFoundError
65
65
  nasty_hash.deep_update(key: :pretty, value: :super_duper, error_on_missing: false) # => {:my=>{:pretty=>[{:nasty=>:hash}, nil], :is=>{:pretty=>:nasty}}, :huh=>:where_am_i}
66
66
 
67
+ # Pass through parent flag for scoping
68
+ nasty_hash.deep_update(key: :pretty, value: :super_duper, error_on_uniqueness: false, parent: :is) # => {:my=>{:pretty=>[{:nasty=>:hash}, nil], :is=>{:pretty=>:super_duper}}}
69
+
67
70
  ```
68
71
  ---
69
72
  #### deep_remove
@@ -80,6 +83,9 @@ nasty_hash.deep_remove(key: :nasty) # => {:my=>{:pretty=>[{}, nil], :is=>{:prett
80
83
  # Errors on uniqueness
81
84
  nasty_hash.deep_remove(key: :pretty) # => throws KeyNotUniqueError
82
85
  nasty_hash.deep_remove(key: :pretty, error_on_uniqueness: false) # => {:my=>{:is=>{}}}
86
+
87
+ # Pass through parent flag for scoping
88
+ nasty_hash.deep_remove(key: :pretty, error_on_uniqueness: false, parent: :is) # => { my: { pretty: [{ nasty: :hash }, nil], is: { } } }
83
89
  ```
84
90
  ---
85
91
  #### deep_find
@@ -93,6 +99,9 @@ nasty_hash = { my: { pretty: [{ nasty: :hash }, nil], is: { pretty: :nasty } } }
93
99
 
94
100
  nasty_hash.deep_find(key: :nasty) # => [:hash]
95
101
  nasty_hash.deep_find(key: :pretty) # => [{:nasty=>:hash}, nil, :nasty]
102
+
103
+ # Pass through parent flag for scoping
104
+ nasty_hash.deep_find(key: :pretty, parent: :is) # => [:nasty]
96
105
  ```
97
106
  ---
98
107
  #### deep_compact
@@ -131,6 +140,26 @@ nasty_hash.deep_contains?(key: :nasty) # => true
131
140
  nasty_hash.deep_contains?(key: :super_nasty) # => false
132
141
  ```
133
142
 
143
+ ### Scoping
144
+ HashMiner supports partial updating of a hash based on the parent key.
145
+
146
+ Supported methods:
147
+ - `deep_find`
148
+ - `deep_update`
149
+ - `deep_remove`
150
+
151
+ Pass through a `parent` key to any of these methods as either an `Array`|`String`|`Symbol`
152
+
153
+ ```ruby
154
+ require 'hash_miner'
155
+
156
+ nasty_hash = { my: { pretty: [{ nasty: :hash }, nil], is: { pretty: :nasty } } }
157
+
158
+ nasty_hash.deep_find(key: :pretty, parent: :is) # => [:nasty]
159
+ nasty_hash.deep_find(key: :pretty, parent: [:is]) # => [:nasty]
160
+ nasty_hash.deep_find(key: :pretty, parent: [:my, :is]) # => [{:nasty=>:hash}, nil, :nasty]
161
+ ```
162
+
134
163
  ## Development
135
164
 
136
165
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -50,21 +50,14 @@ class Hash
50
50
  #
51
51
  # @param key [String, Symbol]
52
52
  # @param hash [Hash]
53
+ # @param parent [String, Symbol, Array]
53
54
  #
54
55
  # @return [Array] with all values that match the key
55
- def deep_find(key:, hash: self)
56
+ def deep_find(key:, hash: self, parent: nil)
56
57
  return nil unless hash.is_a? Hash
57
58
  return nil unless hash.deep_contains?(key: key)
58
59
 
59
- hash.filter_map do |k, v|
60
- if k.eql? key
61
- v
62
- elsif v.is_a?(Hash)
63
- deep_find(hash: v, key: key)
64
- elsif v.is_a?(Array)
65
- [v.filter_map { |i| deep_find(hash: i, key: key) }]
66
- end
67
- end.flatten
60
+ parent ? deep_find_parent_logic(hash: self, key: key, parent: parent) : deep_find_logic(hash: self, key: key)
68
61
  end
69
62
 
70
63
  # Removed nil/empty from Hash
@@ -95,9 +88,10 @@ class Hash
95
88
  # @param value [String] the value to be set
96
89
  # @param error_on_missing [Boolean] error if key missing
97
90
  # @param error_on_uniqueness [Boolean] error if key not unique
91
+ # @param parent [String, Symbol, Array] the parent key for the key you want to update
98
92
  #
99
93
  # @return [Hash] the object with specified key updated.
100
- def deep_update(key:, value:, error_on_missing: true, error_on_uniqueness: true)
94
+ def deep_update(key:, value:, error_on_missing: true, error_on_uniqueness: true, parent: nil)
101
95
  return self unless is_a? Hash
102
96
 
103
97
  if error_on_uniqueness && (deep_count(key: key) > 1)
@@ -117,15 +111,42 @@ class Hash
117
111
  return hash
118
112
  end
119
113
 
120
- to_h do |k, v|
114
+ if parent
115
+ deep_update_parent_flow(hash: self, key: key, value: value, parent: parent)
116
+ else
117
+ deep_update_logic(hash: self, key: key, value: value)
118
+ end
119
+ end
120
+
121
+ # Removes specified key from nested hash.
122
+ #
123
+ # @param key [String] the key to remove.
124
+ # @param error_on_uniqueness [Boolean] error if key not unique
125
+ # @param parent [String, Symbol, Array] the parent key for the key you want to update
126
+ #
127
+ # @return [Hash] the object with specified key removed.
128
+ def deep_remove(key:, error_on_uniqueness: true, parent: nil)
129
+ return self unless is_a? Hash
130
+
131
+ if error_on_uniqueness && (deep_count(key: key) > 1)
132
+ raise KeyNotUniqueError, "Key: '#{key}' not unique | Pass 'error_on_uniqueness: false' if you do not care"
133
+ end
134
+
135
+ parent ? deep_remove_logic_parent(hash: self, key: key, parent: parent) : deep_remove_logic(hash: self, key: key)
136
+ end
137
+
138
+ private
139
+
140
+ def deep_update_logic(hash:, key:, value:)
141
+ hash.to_h do |k, v|
121
142
  if k.eql?(key)
122
143
  [k, value]
123
144
  elsif v.is_a?(Hash) && v.deep_contains?(key: key)
124
- [k, v.deep_update(key: key, value: value, error_on_missing: error_on_missing, error_on_uniqueness: error_on_uniqueness)]
145
+ [k, deep_update_logic(key: key, value: value, hash: v)]
125
146
  elsif v.is_a?(Array)
126
147
  [k, v.map do |item|
127
148
  if item.is_a?(Hash) && item.deep_contains?(key: key)
128
- item.deep_update(key: key, value: value, error_on_missing: error_on_missing, error_on_uniqueness: error_on_uniqueness)
149
+ deep_update_logic(key: key, value: value, hash: item)
129
150
  else
130
151
  item
131
152
  end
@@ -136,28 +157,50 @@ class Hash
136
157
  end
137
158
  end
138
159
 
139
- # Removes specified key from nested hash.
140
- #
141
- # @param key [String] the key to remove.
142
- #
143
- # @return [Hash] the object with specified key removed.
144
- def deep_remove(key:, error_on_uniqueness: true)
145
- return self unless is_a? Hash
146
-
147
- if error_on_uniqueness && (deep_count(key: key) > 1)
148
- raise KeyNotUniqueError, "Key: '#{key}' not unique | Pass 'error_on_uniqueness: false' if you do not care"
160
+ def deep_update_parent_flow(hash:, key:, value:, parent:)
161
+ hash.to_h do |k, v|
162
+ if (parent.is_a?(Array) && parent.include?(k)) || parent.eql?(k)
163
+ case v
164
+ when Hash
165
+ [k, deep_update_logic(hash: v, key: key, value: value)]
166
+ when Array
167
+ [k, v.map do |item|
168
+ if item.is_a?(Hash) && item.deep_contains?(key: key)
169
+ deep_update_logic(key: key, value: value, hash: item)
170
+ else
171
+ item
172
+ end
173
+ end]
174
+ else
175
+ [k, v]
176
+ end
177
+ elsif v.is_a?(Hash) && v.deep_contains?(key: key)
178
+ [k, deep_update_parent_flow(hash: v, key: key, value: value, parent: parent)]
179
+ elsif v.is_a?(Array)
180
+ [k, v.map do |item|
181
+ if item.is_a?(Hash) && item.deep_contains?(key: key)
182
+ deep_update_parent_flow(hash: item, key: key, value: value, parent: parent)
183
+ else
184
+ item
185
+ end
186
+ end]
187
+ else
188
+ [k, v]
189
+ end
149
190
  end
191
+ end
150
192
 
193
+ def deep_remove_logic(hash:, key:)
151
194
  # filter_map removes nil from Array
152
- filter_map do |k, v|
195
+ hash.filter_map do |k, v|
153
196
  if k.eql? key
154
197
  nil
155
- elsif v.is_a?(Hash)
156
- [k, v.deep_remove(key: key, error_on_uniqueness: error_on_uniqueness)]
198
+ elsif v.is_a?(Hash) && v.deep_contains?(key: key)
199
+ [k, deep_remove_logic(key: key, hash: v)]
157
200
  elsif v.is_a?(Array)
158
201
  [k, v.map do |item|
159
202
  if item.is_a?(Hash) && item.deep_contains?(key: key)
160
- item.deep_remove(key: key, error_on_uniqueness: error_on_uniqueness)
203
+ deep_remove_logic(key: key, hash: item)
161
204
  else
162
205
  item
163
206
  end
@@ -167,4 +210,72 @@ class Hash
167
210
  end
168
211
  end.to_h
169
212
  end
213
+
214
+ def deep_remove_logic_parent(hash:, key:, parent:)
215
+ hash.filter_map do |k, v|
216
+ if (parent.is_a?(Array) && parent.include?(k)) || parent.eql?(k)
217
+ case v
218
+ when Hash
219
+ [k, deep_remove_logic(key: key, hash: v)]
220
+ when Array
221
+ [k, v.map do |item|
222
+ if item.is_a?(Hash) && item.deep_contains?(key: key)
223
+ deep_remove_logic(key: key, hash: item)
224
+ else
225
+ item
226
+ end
227
+ end]
228
+ else
229
+ [k, v]
230
+ end
231
+ elsif v.is_a?(Hash) && v.deep_contains?(key: key)
232
+ [k, deep_remove_logic_parent(hash: v, key: key, parent: parent)]
233
+ elsif v.is_a?(Array)
234
+ [k, v.map do |item|
235
+ if item.is_a?(Hash) && item.deep_contains?(key: key)
236
+ deep_remove_logic_parent(hash: item, key: key, parent: parent)
237
+ else
238
+ item
239
+ end
240
+ end]
241
+ else
242
+ [k, v]
243
+ end
244
+ end.to_h
245
+ end
246
+
247
+ def deep_find_logic(hash:, key:)
248
+ hash.filter_map do |k, v|
249
+ if k.eql? key
250
+ v
251
+ elsif v.is_a?(Hash)
252
+ deep_find_logic(hash: v, key: key)
253
+ elsif v.is_a?(Array)
254
+ [v.filter_map do |item|
255
+ deep_find_logic(hash: i, key: key) if item.is_a?(Hash) && item.deep_contains?(key: key)
256
+ end]
257
+ end
258
+ end.flatten
259
+ end
260
+
261
+ def deep_find_parent_logic(hash:, key:, parent:)
262
+ hash.filter_map do |k, v|
263
+ if (parent.is_a?(Array) && parent.include?(k)) || parent.eql?(k)
264
+ case v
265
+ when Hash
266
+ deep_find_logic(key: key, hash: v)
267
+ when Array
268
+ [v.filter_map do |item|
269
+ deep_find_logic(key: key, hash: item) if item.is_a?(Hash) && item.deep_contains?(key: key)
270
+ end]
271
+ end
272
+ elsif v.is_a?(Hash) && v.deep_contains?(key: key)
273
+ deep_find_parent_logic(hash: v, key: key, parent: parent)
274
+ elsif v.is_a?(Array)
275
+ [v.filter_map do |item|
276
+ deep_find_parent_logic(hash: item, key: key, parent: parent) if item.is_a?(Hash) && item.deep_contains?(key: key)
277
+ end]
278
+ end
279
+ end.flatten
280
+ end
170
281
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HashMiner
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'
5
5
  end
data/lib/hash_miner.rb CHANGED
@@ -7,8 +7,10 @@ require_relative 'hash_miner/errors'
7
7
  require 'pry'
8
8
  require 'logger'
9
9
 
10
+ # HashMiner gem
10
11
  module HashMiner
11
12
  class Error < StandardError; end
12
13
 
13
14
  LOG = Logger.new($stdout)
15
+ LOG.formatter = proc { |severity, datetime, _progname, msg| "[#{datetime} #{severity} HashMiner] -- #{msg}\n" }
14
16
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hash_miner
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - alexo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-05 00:00:00.000000000 Z
11
+ date: 2022-07-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Additional Hash methods to help navigate the tricky world of nested Hashes.
14
14
  email: