hash_op 0.1.0 → 0.2.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/.codeclimate.yml +4 -0
- data/.rubocop.yml +12 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +2 -2
- data/README.md +273 -6
- data/lib/hash_op/{deep_access.rb → deep.rb} +35 -9
- data/lib/hash_op/filter.rb +12 -12
- data/lib/hash_op/grouping.rb +2 -2
- data/lib/hash_op/mapping.rb +27 -12
- data/lib/hash_op/math.rb +33 -50
- data/lib/hash_op/merge.rb +5 -5
- data/lib/hash_op/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4ee4ebeea9d4222628d2e8bc1a35ac27ff6b219
|
4
|
+
data.tar.gz: 9cc047ece2b3c87a1baf144f17f3a3434da43afc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb145f094b7bd06a0f77ad34b81e0b674d6bb74daa5358a1b60655dc171be67e1113ce39980aee0a99dd26aafaf289e8a04c293930ebebaf722a874868c8fd7f
|
7
|
+
data.tar.gz: 302dede0a9005b1284bb3d405e35937defebc45ad6744f635b7bcb8b15a351460ec43487031a46e6439ff795296fb8da87c3c4571691b0f690e78ce32ab93fec
|
data/.codeclimate.yml
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Metrics/LineLength:
|
2
|
+
Max: 120
|
3
|
+
Style/StringLiterals:
|
4
|
+
EnforcedStyle: single_quotes
|
5
|
+
Style/EmptyLinesAroundBlockBody:
|
6
|
+
Enabled: false
|
7
|
+
Style/EmptyLinesAroundClassBody:
|
8
|
+
Enabled: false
|
9
|
+
Style/EmptyLinesAroundClassBody:
|
10
|
+
Enabled: false
|
11
|
+
Style/AlignParameters:
|
12
|
+
EnforcedStyle: with_fixed_indentation
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
A Ruby library of functions to access and manipulate hash data structures.
|
5
5
|
|
6
|
-
[](https://travis-ci.org/rchampourlier/hash_op)
|
7
7
|
[](https://codeclimate.com/github/rchampourlier/hash_op)
|
8
8
|
[](https://coveralls.io/r/rchampourlier/hash_op)
|
9
9
|
|
@@ -27,15 +27,13 @@ Or install it yourself as:
|
|
27
27
|
|
28
28
|
## Available operations
|
29
29
|
|
30
|
-
_See specs for more details on each operation._
|
31
|
-
|
32
30
|
### Deep Access
|
33
31
|
|
34
32
|
```ruby
|
35
|
-
HashOp::
|
33
|
+
HashOp::Deep.fetch({a: {b: {c: 1}}}, :'a.b.c')
|
36
34
|
=> 1
|
37
35
|
|
38
|
-
HashOp::
|
36
|
+
HashOp::Deep.merge({ a: { b: { c: 1 } } }, :'a.b.c', 2)
|
39
37
|
=> {
|
40
38
|
:a => {
|
41
39
|
:b => {
|
@@ -44,7 +42,264 @@ HashOp::DeepAccess.merge({ a: { b: { c: 1 } } }, :'a.b.c', 2)
|
|
44
42
|
}
|
45
43
|
}
|
46
44
|
```
|
47
|
-
|
45
|
+
|
46
|
+
### Filter
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
hashes = [
|
50
|
+
{ value: 123, regexp: "itsamatch", proc: "1+1" },
|
51
|
+
{ value: 123, regexp: "abcdef", proc: "1+2" },
|
52
|
+
{ value: 234, regexp: "abcdef", proc: "1+2" }
|
53
|
+
]
|
54
|
+
criteria = {
|
55
|
+
path: :value,
|
56
|
+
matching_object: 123
|
57
|
+
}
|
58
|
+
HashOp::Filter.filter(hashes, { value: 123 })
|
59
|
+
=> [
|
60
|
+
[0] {
|
61
|
+
:proc => "1+1",
|
62
|
+
:regexp => "itsamatch",
|
63
|
+
:value => 123
|
64
|
+
},
|
65
|
+
[1] {
|
66
|
+
:proc => "1+2",
|
67
|
+
:regexp => "abcdef",
|
68
|
+
:value => 123
|
69
|
+
}
|
70
|
+
]
|
71
|
+
HashOp::Filter.filter(hashes, { value: 123, regexp: /match/ })
|
72
|
+
=> [
|
73
|
+
[0] {
|
74
|
+
:proc => "1+1",
|
75
|
+
:regexp => "itsamatch",
|
76
|
+
:value => 123
|
77
|
+
}
|
78
|
+
]
|
79
|
+
HashOp::Filter.filter(hashes, { proc: ->(x) { eval(x) == 2 } })
|
80
|
+
=> [
|
81
|
+
[0] {
|
82
|
+
:proc => "1+1",
|
83
|
+
:regexp => "itsamatch",
|
84
|
+
:value => 123
|
85
|
+
}
|
86
|
+
]
|
87
|
+
|
88
|
+
Internally, `HashOp::Filter::filter` uses
|
89
|
+
`HashOp::Filter::match?(hash, criteria)` which you can
|
90
|
+
use too.
|
91
|
+
```
|
92
|
+
|
93
|
+
### Mapping
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
hash = {a: { b: { c: 1 } } }
|
97
|
+
mapping = { r: { path: :'a.b.c' } }
|
98
|
+
HashOp::Mapping.apply_mapping(hash, mapping)
|
99
|
+
=> {
|
100
|
+
:r => 1
|
101
|
+
}
|
102
|
+
|
103
|
+
hash = {
|
104
|
+
raw: { deep: 'raw_value' },
|
105
|
+
time: '2015-07-06 03:37:13 +0200',
|
106
|
+
mapped_hash: {
|
107
|
+
raw: { deep: 'deep_raw_value' },
|
108
|
+
time: '2014-07-06 03:37:13 +0200'
|
109
|
+
},
|
110
|
+
parseable_string: 'a=1;b=2;t=2013-07-06 03:37:13 +0200',
|
111
|
+
array: [
|
112
|
+
'2015-07-06 03:37:13 +0200',
|
113
|
+
'2014-07-06 03:37:13 +0200',
|
114
|
+
'2013-07-06 03:37:13 +0200'
|
115
|
+
]
|
116
|
+
}
|
117
|
+
mapping = {
|
118
|
+
raw: { path: :'raw.deep' },
|
119
|
+
time: { path: :time, type: :time },
|
120
|
+
raw_from_mapped_hash: {
|
121
|
+
path: :'mapped_hash.raw.deep',
|
122
|
+
},
|
123
|
+
time_from_mapped_hash: {
|
124
|
+
path: :'mapped_hash.time',
|
125
|
+
type: :time
|
126
|
+
},
|
127
|
+
values_from_parseable_string: {
|
128
|
+
path: :parseable_string,
|
129
|
+
type: :parseable_string,
|
130
|
+
parsing_mapping: {
|
131
|
+
value: { regexp: 'a=(\d)+;' },
|
132
|
+
time: {
|
133
|
+
regexp: 't=(.*)$',
|
134
|
+
type: :time
|
135
|
+
}
|
136
|
+
}
|
137
|
+
},
|
138
|
+
times_from_array: {
|
139
|
+
type: :array,
|
140
|
+
path: :array,
|
141
|
+
item_mapping: { type: :time }
|
142
|
+
}
|
143
|
+
}
|
144
|
+
HashOp::Mapping.apply_mapping(hash, mapping)
|
145
|
+
=> {
|
146
|
+
:raw => "raw_value",
|
147
|
+
:raw_from_mapped_hash => "deep_raw_value",
|
148
|
+
:time => 2015-07-06 03:37:13 +0200,
|
149
|
+
:time_from_mapped_hash => 2014-07-06 03:37:13 +0200,
|
150
|
+
:times_from_array => [
|
151
|
+
[0] 2015-07-06 03:37:13 +0200,
|
152
|
+
[1] 2014-07-06 03:37:13 +0200,
|
153
|
+
[2] 2013-07-06 03:37:13 +0200
|
154
|
+
],
|
155
|
+
:values_from_parseable_string => {
|
156
|
+
:time => 2013-07-06 03:37:13 +0200,
|
157
|
+
:value => "1"
|
158
|
+
}
|
159
|
+
}
|
160
|
+
```
|
161
|
+
|
162
|
+
### Grouping
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
hashes = [
|
166
|
+
{
|
167
|
+
grouping_path: 'A',
|
168
|
+
value: 1,
|
169
|
+
node: { 'deep_grouping_path': 'AA' }
|
170
|
+
},
|
171
|
+
{
|
172
|
+
grouping_path: 'B',
|
173
|
+
value: 2,
|
174
|
+
node: { 'deep_grouping_path': 'BB' }
|
175
|
+
},
|
176
|
+
{
|
177
|
+
grouping_path: 'A',
|
178
|
+
value: 3,
|
179
|
+
node: { 'deep_grouping_path': 'AB' }
|
180
|
+
},
|
181
|
+
{
|
182
|
+
grouping_path: 'A',
|
183
|
+
value: 4,
|
184
|
+
node: { 'deep_grouping_path': 'AA' }
|
185
|
+
}
|
186
|
+
]
|
187
|
+
HashOp::Grouping.group_on_path(hashes, :grouping_path)
|
188
|
+
=> {
|
189
|
+
"A" => [
|
190
|
+
[0] {
|
191
|
+
:grouping_path => "A",
|
192
|
+
:node => {
|
193
|
+
:deep_grouping_path => "AA"
|
194
|
+
},
|
195
|
+
:value => 1
|
196
|
+
},
|
197
|
+
[1] {
|
198
|
+
:grouping_path => "A",
|
199
|
+
:node => {
|
200
|
+
:deep_grouping_path => "AB"
|
201
|
+
},
|
202
|
+
:value => 3
|
203
|
+
},
|
204
|
+
[2] {
|
205
|
+
:grouping_path => "A",
|
206
|
+
:node => {
|
207
|
+
:deep_grouping_path => "AA"
|
208
|
+
},
|
209
|
+
:value => 4
|
210
|
+
}
|
211
|
+
],
|
212
|
+
"B" => [
|
213
|
+
[0] {
|
214
|
+
:grouping_path => "B",
|
215
|
+
:node => {
|
216
|
+
:deep_grouping_path => "BB"
|
217
|
+
},
|
218
|
+
:value => 2
|
219
|
+
}
|
220
|
+
]
|
221
|
+
}
|
222
|
+
HashOp::Grouping.group_on_path(hashes, :'node.deep_grouping_path')
|
223
|
+
=> {
|
224
|
+
"AA" => [
|
225
|
+
[0] {
|
226
|
+
:grouping_path => "A",
|
227
|
+
:node => {
|
228
|
+
:deep_grouping_path => "AA"
|
229
|
+
},
|
230
|
+
:value => 1
|
231
|
+
},
|
232
|
+
[1] {
|
233
|
+
:grouping_path => "A",
|
234
|
+
:node => {
|
235
|
+
:deep_grouping_path => "AA"
|
236
|
+
},
|
237
|
+
:value => 4
|
238
|
+
}
|
239
|
+
],
|
240
|
+
"AB" => [
|
241
|
+
[0] {
|
242
|
+
:grouping_path => "A",
|
243
|
+
:node => {
|
244
|
+
:deep_grouping_path => "AB"
|
245
|
+
},
|
246
|
+
:value => 3
|
247
|
+
}
|
248
|
+
],
|
249
|
+
"BB" => [
|
250
|
+
[0] {
|
251
|
+
:grouping_path => "B",
|
252
|
+
:node => {
|
253
|
+
:deep_grouping_path => "BB"
|
254
|
+
},
|
255
|
+
:value => 2
|
256
|
+
}
|
257
|
+
]
|
258
|
+
}
|
259
|
+
HashOp::Grouping.group_on_paths(hashes, [:grouping_path, :'node.deep_grouping_path'])
|
260
|
+
=> {
|
261
|
+
"A" => {
|
262
|
+
"AA" => [
|
263
|
+
[0] {
|
264
|
+
:grouping_path => "A",
|
265
|
+
:node => {
|
266
|
+
:deep_grouping_path => "AA"
|
267
|
+
},
|
268
|
+
:value => 1
|
269
|
+
},
|
270
|
+
[1] {
|
271
|
+
:grouping_path => "A",
|
272
|
+
:node => {
|
273
|
+
:deep_grouping_path => "AA"
|
274
|
+
},
|
275
|
+
:value => 4
|
276
|
+
}
|
277
|
+
],
|
278
|
+
"AB" => [
|
279
|
+
[0] {
|
280
|
+
:grouping_path => "A",
|
281
|
+
:node => {
|
282
|
+
:deep_grouping_path => "AB"
|
283
|
+
},
|
284
|
+
:value => 3
|
285
|
+
}
|
286
|
+
]
|
287
|
+
},
|
288
|
+
"B" => {
|
289
|
+
"BB" => [
|
290
|
+
[0] {
|
291
|
+
:grouping_path => "B",
|
292
|
+
:node => {
|
293
|
+
:deep_grouping_path => "BB"
|
294
|
+
},
|
295
|
+
:value => 2
|
296
|
+
}
|
297
|
+
]
|
298
|
+
}
|
299
|
+
}
|
300
|
+
```
|
301
|
+
|
302
|
+
_See specs for more details on each operation and operations not documented here._
|
48
303
|
|
49
304
|
## Development
|
50
305
|
|
@@ -55,3 +310,15 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
55
310
|
## Contributing
|
56
311
|
|
57
312
|
Bug reports and pull requests are welcome on GitHub at https://github.com/rchampourlier/hash_op. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) [code of conduct](CODE_OF_CONDUCT.md).
|
313
|
+
|
314
|
+
## Revisions
|
315
|
+
|
316
|
+
### 0.2.0
|
317
|
+
|
318
|
+
- Removed some operations that made no real sense (`Math.sum` and `Math.sum_two`).
|
319
|
+
- Renamed `DeepAccess` to `Deep`.
|
320
|
+
- Renamed `Merge.merge` to `Merge.flat` and `Merge.merge_by_group` to `Merge.by_group`.
|
321
|
+
|
322
|
+
### 0.1.0
|
323
|
+
|
324
|
+
Initial version
|
@@ -5,19 +5,19 @@ require 'active_support/all'
|
|
5
5
|
# through a path in the form of 'a.b.c' or an array
|
6
6
|
# of segments ['a', 'b', 'c'].
|
7
7
|
module HashOp
|
8
|
-
module
|
8
|
+
module Deep
|
9
9
|
|
10
10
|
# Examples:
|
11
11
|
# h = {a: {b: {c: 1}}}
|
12
|
-
# HashOp::
|
13
|
-
# HashOp::
|
14
|
-
# HashOp::
|
15
|
-
# HashOp::
|
16
|
-
# HashOp::
|
17
|
-
# HashOp::
|
12
|
+
# HashOp::Deep.fetch(h, :a) # => {:b=>{:c=>1}}
|
13
|
+
# HashOp::Deep.fetch(h, :'a.b') # => {:c=>1}
|
14
|
+
# HashOp::Deep.fetch(h, :'a.b.c') # => 1
|
15
|
+
# HashOp::Deep.fetch(h, [:a]) # => {:b=>{:c=>1}}
|
16
|
+
# HashOp::Deep.fetch(h, [:a, :b, :c]) # => 1
|
17
|
+
# HashOp::Deep.fetch(h, :'b.c.a') # => nil
|
18
18
|
#
|
19
19
|
def fetch(hash, path)
|
20
|
-
|
20
|
+
fail ArgumentError, 'First argument must be an Hash' unless hash.is_a?(Hash)
|
21
21
|
if path.class.in? [String, Symbol]
|
22
22
|
fetch_with_deep_key(hash, path)
|
23
23
|
elsif path.is_a? Array
|
@@ -28,8 +28,16 @@ module HashOp
|
|
28
28
|
end
|
29
29
|
module_function :fetch
|
30
30
|
|
31
|
+
# Examples:
|
32
|
+
# h = {}
|
33
|
+
# h = HashOp::Deep.merge(h, :a, 1)
|
34
|
+
# => { :a => 1 }
|
35
|
+
# h = HashOp::Deep.merge(h, :'a.b', 2)
|
36
|
+
# => { :a => { :b => 2 }
|
37
|
+
# h = HashOp::Deep.merge(h, :b, 3)
|
38
|
+
# => { :a => { :b => 2 }, :b => 3 }
|
31
39
|
def merge(hash, path, value)
|
32
|
-
|
40
|
+
fail ArgumentError, 'First argument must be an Hash' unless hash.is_a? Hash
|
33
41
|
if path.class.in? [String, Symbol]
|
34
42
|
merge_with_deep_key(hash, path, value)
|
35
43
|
elsif path.is_a? Array
|
@@ -40,6 +48,24 @@ module HashOp
|
|
40
48
|
end
|
41
49
|
module_function :merge
|
42
50
|
|
51
|
+
# Example:
|
52
|
+
# hash = { a: { :b => 1, 'c' => 2 }, d: 0 }
|
53
|
+
# HashOp::Deep.paths(hash)
|
54
|
+
# => [[:a, :b], [:a, 'c' ], [:d]]
|
55
|
+
def paths(hash)
|
56
|
+
r = []
|
57
|
+
hash.each do |key, value|
|
58
|
+
if value.is_a?(Hash)
|
59
|
+
paths(value).each do |deep_key|
|
60
|
+
r << [key] + Array(deep_key)
|
61
|
+
end
|
62
|
+
else r << Array(key)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
r.uniq
|
66
|
+
end
|
67
|
+
module_function :paths
|
68
|
+
|
43
69
|
private
|
44
70
|
|
45
71
|
def fetch_with_deep_key(hash, deep_key)
|
data/lib/hash_op/filter.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'hash_op/
|
1
|
+
require 'hash_op/deep'
|
2
2
|
|
3
3
|
# Performs filtering operation on hash or array of hashes
|
4
4
|
module HashOp
|
@@ -7,6 +7,8 @@ module HashOp
|
|
7
7
|
# Filters an array of hashes according to criteria
|
8
8
|
# on the values of each hash.
|
9
9
|
#
|
10
|
+
# Check the README for examples.
|
11
|
+
#
|
10
12
|
# @param [Array] hashes array of hashes to be filtered
|
11
13
|
# @param [Hash] criteria the method uses ::match?, see
|
12
14
|
# definition for more details
|
@@ -24,17 +26,17 @@ module HashOp
|
|
24
26
|
# @param [Hash] hash the hash to be filtered
|
25
27
|
# @param [String, Symbol] the path of the array of hashes
|
26
28
|
# inside hash to be filtered. Accessed through
|
27
|
-
# HashOp::
|
29
|
+
# HashOp::Deep (path like 'path.to.some.key').
|
28
30
|
# @param [Hash] criteria to filter on (performed through
|
29
31
|
# ::filter, so see the method for more details)
|
30
32
|
# @return [Hash] the hash with values in array at path
|
31
33
|
# filtered according to criteria
|
32
34
|
def filter_deep(hash, path, criteria = {})
|
33
|
-
array = HashOp::
|
35
|
+
array = HashOp::Deep.fetch hash, path
|
34
36
|
raise "Can\'t filter hash at path \"#{path}\", value is not an array" unless array.is_a?(Array)
|
35
37
|
|
36
38
|
filtered_array = filter(array, criteria)
|
37
|
-
HashOp::
|
39
|
+
HashOp::Deep.merge hash, path, filtered_array
|
38
40
|
end
|
39
41
|
module_function :filter_deep
|
40
42
|
|
@@ -43,7 +45,7 @@ module HashOp
|
|
43
45
|
# each criteria is an hash
|
44
46
|
# { path => matching_object }, where:
|
45
47
|
# - path [String, Symbol] is used to access the value
|
46
|
-
# in the filtered object (through HashOp::
|
48
|
+
# in the filtered object (through HashOp::Deep::fetch)
|
47
49
|
# - matching_object [Object] the object defining the
|
48
50
|
# match:
|
49
51
|
# * a Proc which will be called with the value and
|
@@ -54,20 +56,18 @@ module HashOp
|
|
54
56
|
# * any other value will be matched against the
|
55
57
|
# equality operator.
|
56
58
|
def match?(hash, criteria)
|
57
|
-
|
59
|
+
unless hash.is_a?(Hash)
|
60
|
+
fail ArgumentError, 'First argument must be an Hash'
|
61
|
+
end
|
58
62
|
return true if criteria.blank?
|
59
63
|
|
60
64
|
criteria.map do |path, matching_object|
|
61
|
-
value = HashOp::
|
62
|
-
|
65
|
+
value = HashOp::Deep.fetch(hash, path)
|
63
66
|
case
|
64
|
-
|
65
67
|
when matching_object.is_a?(Proc)
|
66
68
|
matching_object.call(value)
|
67
|
-
|
68
69
|
when matching_object.is_a?(Regexp)
|
69
|
-
|
70
|
-
|
70
|
+
(value =~ matching_object).present?
|
71
71
|
else value == matching_object
|
72
72
|
end
|
73
73
|
end.uniq == [true]
|
data/lib/hash_op/grouping.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'hash_op/
|
1
|
+
require 'hash_op/deep'
|
2
2
|
|
3
3
|
# A module to perform group operations on hashes.
|
4
4
|
module HashOp
|
@@ -10,7 +10,7 @@ module HashOp
|
|
10
10
|
# @return [Hash]
|
11
11
|
def group_on_path(hashes, path)
|
12
12
|
hashes.inject({}) do |result, hash|
|
13
|
-
value_at_path = HashOp::
|
13
|
+
value_at_path = HashOp::Deep.fetch(hash, path)
|
14
14
|
result[value_at_path] ||= []
|
15
15
|
result[value_at_path] << hash
|
16
16
|
result
|
data/lib/hash_op/mapping.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'hash_op/
|
1
|
+
require 'hash_op/deep'
|
2
2
|
|
3
3
|
# A module to perform mapping from hash to hash.
|
4
4
|
module HashOp
|
@@ -9,20 +9,35 @@ module HashOp
|
|
9
9
|
# mapping (see example below)
|
10
10
|
# A mapping hash is:
|
11
11
|
# a_key [String or Symbol] => a_mapping_item [Hash]
|
12
|
-
# A mapping item is:
|
13
|
-
# path: [String or Symbol] the path to the value
|
14
|
-
# type: [
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
12
|
+
# A mapping item is composed of:
|
13
|
+
# - path: [String or Symbol] the path to the value
|
14
|
+
# - type: [Symbol] defines how the item will be processed
|
15
|
+
# before being added to the result
|
16
|
+
# - `raw` (defaults, if no type specified): the value
|
17
|
+
# is passed with no transformation
|
18
|
+
# - `time`: the value is parsed using `Time.parse`
|
19
|
+
# (if the parse fails, nil is passed in the result,
|
20
|
+
# no exception raised)
|
21
|
+
# - `mapped_hash`: the value is mapped recursively,
|
22
|
+
# using the `mapping` key as the mapping
|
23
|
+
# - `parseable_string`: the string value is analyzed
|
24
|
+
# using regexps and each value extracted by a regexp
|
25
|
+
# can be recursively processed using mappings
|
26
|
+
# applicable to strings (e.g. time); the mapping
|
27
|
+
# to use for the parsing is defined in
|
28
|
+
# `parsing_mapping` (see the README for an example)
|
29
|
+
# - `array`: recursively apply a mapping over each
|
30
|
+
# item in the array; the mapping for each item is
|
31
|
+
# defined in `item_mapping`
|
32
|
+
# - mapping: for a mapping item of type `mapped_hash`
|
33
|
+
# - parsing_mapping: for a mapping item of type
|
34
|
+
# `parseable_string`
|
35
|
+
# - item_mapping: for a mapping item of type `array`
|
19
36
|
#
|
20
37
|
# Example of a mapping hash:
|
21
38
|
# {
|
22
|
-
#
|
23
|
-
# str: { path: :'root.string' },
|
39
|
+
# raw: { path: :'root.value' },
|
24
40
|
# time: { path: :'root.deep_1.deep_2.time', type: :time },
|
25
|
-
# strings: { path: :'root.array_of_strings' },
|
26
41
|
# mapped_hashes: {
|
27
42
|
# path: :'root.array_of_mapped_hashes',
|
28
43
|
# type: :array,
|
@@ -38,7 +53,7 @@ module HashOp
|
|
38
53
|
mapping_item = mapping[key]
|
39
54
|
path = mapping_item[:path]
|
40
55
|
raise "path not found in mapping item #{mapping_item}" if path.nil?
|
41
|
-
raw = HashOp::
|
56
|
+
raw = HashOp::Deep.fetch(hash, path)
|
42
57
|
processed = process_with_mapping_item(raw, mapping_item)
|
43
58
|
mapped_hash[key] = processed
|
44
59
|
mapped_hash
|
data/lib/hash_op/math.rb
CHANGED
@@ -1,69 +1,52 @@
|
|
1
|
-
require 'hash_op/
|
1
|
+
require 'hash_op/deep'
|
2
2
|
|
3
3
|
# A set of functions to perform mathematical operations
|
4
4
|
# on Hashes.
|
5
5
|
module HashOp
|
6
6
|
module Math
|
7
7
|
|
8
|
-
# @param hashes [Array] of Hash instances
|
9
|
-
# @return [Hash] summing values of the same key
|
10
|
-
def sum(*hashes)
|
11
|
-
hashes.flatten!
|
12
|
-
case hashes.length
|
13
|
-
when 0 then {}
|
14
|
-
when 1 then hashes.first
|
15
|
-
when 2 then sum_two(*hashes)
|
16
|
-
else
|
17
|
-
sum(*[sum_two(*hashes[0..1])] + hashes[2..-1])
|
18
|
-
end
|
19
|
-
end
|
20
|
-
module_function :sum
|
21
|
-
|
22
|
-
def sum_two(hash_a, hash_b)
|
23
|
-
hash_b.each do |key, hash_b_value|
|
24
|
-
if hash_a[key]
|
25
|
-
hash_a[key] += hash_b_value
|
26
|
-
else
|
27
|
-
hash_a[key] = hash_b_value
|
28
|
-
end
|
29
|
-
end
|
30
|
-
hash_a
|
31
|
-
end
|
32
|
-
module_function :sum_two
|
33
|
-
|
34
8
|
# Sum values in an array of hashes by grouping on a given
|
35
9
|
# key.
|
36
10
|
#
|
37
11
|
# Example:
|
38
12
|
# hashes = [
|
39
|
-
# {
|
40
|
-
# {
|
41
|
-
# {
|
13
|
+
# { group_1: :a, group_2: :a, value_1: 1, value_2: 1 },
|
14
|
+
# { group_1: :a, group_2: :b, value_1: 1, value_2: 2 },
|
15
|
+
# { group_1: :a, group_2: :b, value_1: 1, value_2: 2 },
|
16
|
+
# { group_1: :b, group_2: :c, value_1: 1, value_2: 3 }
|
17
|
+
# ]
|
18
|
+
# HashOp::Math.sum_on_groups(hashes,
|
19
|
+
# [:group_1], [:value_1, :value_2]
|
20
|
+
# )
|
21
|
+
# => [
|
22
|
+
# { group_1: :a, value_1: 3, value_2: 5 },
|
23
|
+
# { group_1: :b, value_1: 1, value_2: 3 }
|
24
|
+
# ]
|
25
|
+
# HashOp::Math.sum_on_groups(hashes,
|
26
|
+
# [:group_1, :group_2], [:value_1, :value_2]
|
27
|
+
# )
|
28
|
+
# => [
|
29
|
+
# { group_1: :a, group_2: :a, value_1: 1, value_2: 1 },
|
30
|
+
# { group_1: :a, group_2: :b, value_1: 2, value_2: 4 },
|
31
|
+
# { group_1: :b, group_2: :c, value_1: 1, value_2: 3 }
|
42
32
|
# ]
|
43
|
-
# HashOp::Math.sum_on_groups(hashes, :group, :value)
|
44
|
-
# # => [
|
45
|
-
# # { group: :a, value: 2 },
|
46
|
-
# # { group: :b, value: 1 }
|
47
|
-
# # ]
|
48
33
|
#
|
49
34
|
# @param hashes [Array] the hashes to be summed
|
50
35
|
# @param group_key [Object] the key to use to group items on
|
51
36
|
# @param value_key [Object] the key of the values to sum
|
52
37
|
# @return [Array]
|
53
|
-
def sum_on_groups(hashes,
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
work[hash[group_key]][value_key] += hash[value_key]
|
38
|
+
def sum_on_groups(hashes, grouping_paths, value_paths)
|
39
|
+
grouped_hashes = Grouping.group_on_paths(hashes, grouping_paths)
|
40
|
+
group_paths = Deep.paths(grouped_hashes)
|
41
|
+
result = group_paths.map do |group_path|
|
42
|
+
group_hashes = HashOp::Deep.fetch(grouped_hashes, group_path)
|
43
|
+
group_values = value_paths.map do |value_path|
|
44
|
+
group_value = HashOp::Math.sum_at_path(group_hashes, value_path)
|
45
|
+
{ value_path => group_value }
|
62
46
|
end
|
63
|
-
|
47
|
+
Hash[[grouping_paths, group_path].transpose].merge(Merge.flat(group_values))
|
64
48
|
end
|
65
|
-
|
66
|
-
end
|
49
|
+
end
|
67
50
|
module_function :sum_on_groups
|
68
51
|
|
69
52
|
# Sum values for the specified hashes at the specified path.
|
@@ -76,7 +59,7 @@ module HashOp
|
|
76
59
|
#
|
77
60
|
def sum_at_path(hashes, path, zero = 0)
|
78
61
|
hashes.inject(zero) do |sum, hash|
|
79
|
-
value = HashOp::
|
62
|
+
value = HashOp::Deep.fetch(hash, path) || zero
|
80
63
|
sum + value
|
81
64
|
end
|
82
65
|
end
|
@@ -85,14 +68,14 @@ module HashOp
|
|
85
68
|
# @param [Array] hashes array of Hash
|
86
69
|
# @param [String, Symbol] path to deep value in each hash
|
87
70
|
def deep_min(hashes, path)
|
88
|
-
hashes.map { |hash| HashOp::
|
71
|
+
hashes.map { |hash| HashOp::Deep.fetch hash, path }.min
|
89
72
|
end
|
90
73
|
module_function :deep_min
|
91
74
|
|
92
75
|
# @param [Array] hashes array of Hash
|
93
76
|
# @param [String, Symbol] path to deep value in each hash
|
94
77
|
def deep_max(hashes, path)
|
95
|
-
hashes.map { |hash| HashOp::
|
78
|
+
hashes.map { |hash| HashOp::Deep.fetch hash, path }.max
|
96
79
|
end
|
97
80
|
module_function :deep_max
|
98
81
|
end
|
data/lib/hash_op/merge.rb
CHANGED
@@ -3,19 +3,19 @@ module HashOp
|
|
3
3
|
|
4
4
|
# Merge all specified hashes by merging the second
|
5
5
|
# in the first, the third in the result, and so on.
|
6
|
-
def
|
6
|
+
def flat(hashes)
|
7
7
|
hashes.inject({}) do |result, hash|
|
8
8
|
result.merge hash
|
9
9
|
end
|
10
10
|
end
|
11
|
-
module_function :
|
11
|
+
module_function :flat
|
12
12
|
|
13
13
|
# Merge hashes by grouping them on the
|
14
14
|
# specified key value and merging them all together.
|
15
|
-
def
|
15
|
+
def by_group(hashes, key)
|
16
16
|
groups = hashes.group_by { |h| h[key] }
|
17
|
-
groups.values.map { |g|
|
17
|
+
groups.values.map { |g| flat(g) }
|
18
18
|
end
|
19
|
-
module_function :
|
19
|
+
module_function :by_group
|
20
20
|
end
|
21
21
|
end
|
data/lib/hash_op/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hash_op
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Romain Champourlier
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -101,8 +101,10 @@ executables: []
|
|
101
101
|
extensions: []
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
|
+
- ".codeclimate.yml"
|
104
105
|
- ".gitignore"
|
105
106
|
- ".rspec"
|
107
|
+
- ".rubocop.yml"
|
106
108
|
- ".travis.yml"
|
107
109
|
- CODE_OF_CONDUCT.md
|
108
110
|
- Gemfile
|
@@ -113,7 +115,7 @@ files:
|
|
113
115
|
- bin/setup
|
114
116
|
- hash_op.gemspec
|
115
117
|
- lib/hash_op.rb
|
116
|
-
- lib/hash_op/
|
118
|
+
- lib/hash_op/deep.rb
|
117
119
|
- lib/hash_op/filter.rb
|
118
120
|
- lib/hash_op/grouping.rb
|
119
121
|
- lib/hash_op/mapping.rb
|
@@ -140,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
142
|
version: '0'
|
141
143
|
requirements: []
|
142
144
|
rubyforge_project:
|
143
|
-
rubygems_version: 2.4.
|
145
|
+
rubygems_version: 2.4.5.1
|
144
146
|
signing_key:
|
145
147
|
specification_version: 4
|
146
148
|
summary: A Ruby library of functions to access and manipulate hash data structures.
|