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 +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:
|