hash_miner 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: