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 +4 -4
- data/.rubocop.yml +1 -1
- data/README.md +29 -0
- data/lib/hash_miner/hash.rb +139 -28
- data/lib/hash_miner/version.rb +1 -1
- data/lib/hash_miner.rb +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 914562162950918230de98d58303185f43568f78dfca068bc2f7209f4f96ae2b
|
4
|
+
data.tar.gz: 80e88d5b6d61b41e7aafb12fddf796c9eba40667895fc0cb8e62610e0160e04b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52d4ae8b09bc42cccb5b3fd98180a100074292a98d4b0a0437d62f776e436b69b93c487f15524323995452c1a96b9a214e071bb9735dddfcfff0da251c6b2ed1
|
7
|
+
data.tar.gz: c5af637056ca8c4950846fc497ecac8f34270425f176a2850da0002cebeddbc09c71ed601d381f4e5feb5012bfad84d27db116ce8ab11a3b9413da4e335291f9
|
data/.rubocop.yml
CHANGED
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.
|
data/lib/hash_miner/hash.rb
CHANGED
@@ -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
|
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
|
-
|
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,
|
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
|
-
|
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
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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,
|
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
|
-
|
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
|
data/lib/hash_miner/version.rb
CHANGED
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.
|
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-
|
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:
|