hash_remapper 0.3.0 → 0.4.2
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/.travis.yml +2 -2
- data/README.md +53 -4
- data/exmaples/weather.rb +42 -0
- data/hash_remapper.gemspec +5 -6
- data/lib/hash_remapper.rb +45 -22
- data/lib/hash_remapper/version.rb +4 -0
- metadata +24 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c683439dd0fa1c9ed4829b996ef023248412faa7c40062da5195e6c7f639b610
|
4
|
+
data.tar.gz: 169160ed106c2da575ebbd2e8ee3a09432fdecfcb100af63f899ac08ccafd689
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 10b4f4686588c177567f5f7a0653970b71e18f7bd2919166b9bca568c4ab80a020b69b7ecdd96cce2d3291359c02e7ba6ad43447053f577a768ea41c31360ed0
|
7
|
+
data.tar.gz: 78fdc8e7fba05285373ac1dd8395b6d5aeec76b892ddffb1508cd79c1a34dda19283da5e7014065e30f96ce7eccbba6758231fb93f0868534f584509da600c8a
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
</p>
|
6
6
|
|
7
7
|
> A little lib which maps original keys to the new ones <sup>[*](#features)</sup>
|
8
|
+
> [](https://travis-ci.org/smileart/hash_remapper)
|
8
9
|
|
9
10
|
## Installation
|
10
11
|
|
@@ -29,13 +30,13 @@ Or install it yourself as:
|
|
29
30
|
* preprocess a value with a lambda [3](#lambda)
|
30
31
|
* allows to remap the keys within preprocessing [4](#preprocessing)
|
31
32
|
* allows to keep data subsets only [5](#subset)
|
32
|
-
* allows to include data with the original
|
33
|
+
* allows to include data with the original key names [6](#originals)
|
33
34
|
* allows to use global context to create composite fields [7](#composite)
|
34
35
|
* merges values if the key already exists and supports #merge [8](#merge)
|
35
36
|
* replaces values if the key already exists and doesn't support #merge [9](#replace)
|
36
37
|
* allows to assign static defaults through lambdas [10](#defaults)
|
37
38
|
* allows to remap to the deep values within the context [11](#deep)
|
38
|
-
* allows to create completely new keys [12](#new_keys)
|
39
|
+
* allows to create completely new keys (including nested ones) [12](#new_keys)
|
39
40
|
|
40
41
|
## Usage
|
41
42
|
|
@@ -167,7 +168,7 @@ HashRemapper.remap(
|
|
167
168
|
# }
|
168
169
|
```
|
169
170
|
|
170
|
-
## <a name="originals">6</a>: Include data with the original
|
171
|
+
## <a name="originals">6</a>: Include data with the original key name
|
171
172
|
|
172
173
|
```rb
|
173
174
|
HashRemapper.remap(
|
@@ -266,7 +267,7 @@ HashRemapper.remap(
|
|
266
267
|
# }
|
267
268
|
```
|
268
269
|
|
269
|
-
## <a name="new_keys">12</a>: Create completely new keys
|
270
|
+
## <a name="new_keys">12</a>: Create completely new keys (including nested ones)
|
270
271
|
|
271
272
|
```rb
|
272
273
|
HashRemapper.remap(
|
@@ -280,6 +281,54 @@ HashRemapper.remap(
|
|
280
281
|
# magic_number: 42,
|
281
282
|
# absolutely_new_key: 'shiny new value'
|
282
283
|
# }
|
284
|
+
|
285
|
+
HashRemapper.remap(
|
286
|
+
original_hash,
|
287
|
+
_: [[:nested, :new, :key], :test]
|
288
|
+
)
|
289
|
+
|
290
|
+
# =>
|
291
|
+
# {
|
292
|
+
# nested: {
|
293
|
+
# new: {
|
294
|
+
# key: 42
|
295
|
+
# }
|
296
|
+
# }
|
297
|
+
# }
|
298
|
+
|
299
|
+
|
300
|
+
# mapping a deep target from a deep source (BEWARE an old digging API <= v0.1.0)
|
301
|
+
HashRemapper.remap(
|
302
|
+
original_hash,
|
303
|
+
_: [[:nested, :new, :key], [:nested, :really, :deep]]
|
304
|
+
)
|
305
|
+
|
306
|
+
# =>
|
307
|
+
# {
|
308
|
+
# nested: {
|
309
|
+
# new: {
|
310
|
+
# key: true
|
311
|
+
# }
|
312
|
+
# }
|
313
|
+
# }
|
314
|
+
|
315
|
+
|
316
|
+
# mapping a deep target from a deep source (new digging API >= v0.2.0)
|
317
|
+
HashRemapper.remap(
|
318
|
+
original_hash,
|
319
|
+
_: [[:new, :deeply, :nested, :value], {path: 'recursive.*.number', strict: false, default: 3.14}]
|
320
|
+
)
|
321
|
+
|
322
|
+
# =>
|
323
|
+
# {
|
324
|
+
# new: {
|
325
|
+
# deeply: {
|
326
|
+
# nested: {
|
327
|
+
# value: [21, 42, 3.14]
|
328
|
+
# }
|
329
|
+
# }
|
330
|
+
# }
|
331
|
+
# }
|
283
332
|
```
|
284
333
|
|
285
334
|
## Development
|
data/exmaples/weather.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative '../lib/hash_remapper'
|
3
|
+
|
4
|
+
# https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22
|
5
|
+
weather = JSON.parse('{"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":300,"main":"Drizzle","description":"light intensity drizzle","icon":"09d"}],"base":"stations","main":{"temp":280.32,"pressure":1012,"humidity":81,"temp_min":279.15,"temp_max":281.15},"visibility":10000,"wind":{"speed":4.1,"deg":80},"clouds":{"all":90},"dt":1485789600,"sys":{"type":1,"id":5091,"message":0.0103,"country":"GB","sunrise":1485762037,"sunset":1485794875},"id":2643743,"name":"London","cod":200}')
|
6
|
+
|
7
|
+
k_to_c = ->(kelvin) { "#{(kelvin - 273.15).floor}˚C" } # notice the difference with the next example
|
8
|
+
|
9
|
+
remapped_weather = HashRemapper.remap(
|
10
|
+
weather,
|
11
|
+
# deep keys creation instead of automerge
|
12
|
+
_conditions: [[:current_conditions, :description], {path: 'weather.0', lambda: ->(res){ "#{res[:description]}".capitalize }} ],
|
13
|
+
_temperature: [[:current_conditions, :temperature], {path: 'main.temp', lambda: k_to_c}],
|
14
|
+
|
15
|
+
_clouds: [[:clouds_coverage, :percentage], {path: 'clouds.all'}],
|
16
|
+
'visibility' => ->(vis, _) { [:visibility, "#{vis / 1000}km"] },
|
17
|
+
['sys', 'country'] => ->(country, _) { [[:place, :country], country] },
|
18
|
+
'name' => ->(city, _) { [[:place, :city], city] },
|
19
|
+
'coord' => :geo_coordinates
|
20
|
+
)
|
21
|
+
|
22
|
+
p remapped_weather
|
23
|
+
|
24
|
+
# =================================================================================================================================
|
25
|
+
|
26
|
+
k_to_c = ->(kelvin) { { temperature: "#{(kelvin - 273.15).floor}˚C" } } # notice the difference with the previous example
|
27
|
+
|
28
|
+
remapped_weather = HashRemapper.remap(
|
29
|
+
weather,
|
30
|
+
# automerge on "conflict"
|
31
|
+
_conditions: [:current_conditions, {path: 'weather.0', lambda: ->(res){ { description: "#{res[:description]}".capitalize }}} ],
|
32
|
+
_temperature: [:current_conditions, {path: 'main.temp', lambda: k_to_c}], # automerge
|
33
|
+
_clouds: [[:clouds_coverage, :percentage], {path: 'clouds.all'}],
|
34
|
+
'visibility' => ->(vis, _) { [:visibility, "#{vis / 1000}km"] },
|
35
|
+
['sys', 'country'] => ->(country, _) { [[:place, :country], country] },
|
36
|
+
'name' => ->(city, _) { [[:place, :city], city] },
|
37
|
+
|
38
|
+
# notice the nested HashRemapper (alternatively https://devdocs.io/rails~5.0/hash#method-i-deep_symbolize_keys could be used on overall result)
|
39
|
+
'coord' => ->(coord, _) { [:geo_coordinates, HashRemapper.remap(coord, 'lon' => :lon, 'lat' => :lat)] },
|
40
|
+
)
|
41
|
+
|
42
|
+
p remapped_weather
|
data/hash_remapper.gemspec
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# coding: utf-8
|
3
3
|
|
4
|
-
lib = File.expand_path('
|
4
|
+
lib = File.expand_path('../lib', __FILE__)
|
5
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
6
|
|
7
|
-
|
8
|
-
require_relative './lib/hash_remapper'
|
7
|
+
require 'hash_remapper/version'
|
9
8
|
|
10
|
-
Gem::Specification.new do |spec|
|
9
|
+
HashRemapper::GEMSPEC = Gem::Specification.new do |spec|
|
11
10
|
spec.name = 'hash_remapper'
|
12
11
|
spec.version = HashRemapper::VERSION
|
13
12
|
spec.authors = ['Serge Bedzhyk']
|
@@ -26,6 +25,8 @@ Gem::Specification.new do |spec|
|
|
26
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
26
|
spec.require_paths = ['lib']
|
28
27
|
|
28
|
+
spec.add_dependency 'hash_digger', '~> 0.0', '>= 0.0.4'
|
29
|
+
|
29
30
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
30
31
|
spec.add_development_dependency 'byebug', '~> 11.0'
|
31
32
|
spec.add_development_dependency 'inch', '>= 0.9.0.rc1'
|
@@ -36,6 +37,4 @@ Gem::Specification.new do |spec|
|
|
36
37
|
spec.add_development_dependency 'rubygems-tasks', '~> 0.2'
|
37
38
|
spec.add_development_dependency 'simplecov', '~> 0.15'
|
38
39
|
spec.add_development_dependency 'yard', '~> 0.9'
|
39
|
-
|
40
|
-
spec.add_runtime_dependency 'hash_digger', '~> 0.0'
|
41
40
|
end
|
data/lib/hash_remapper.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
require 'set'
|
4
4
|
require 'hash_digger'
|
5
5
|
|
6
|
+
require 'letters' if ENV['DEBUG']
|
7
|
+
require 'byebug' if ENV['DEBUG']
|
8
|
+
|
6
9
|
# Utility class to map original Hash keys to the new ones
|
7
10
|
class HashRemapper
|
8
|
-
# Current Library Version
|
9
|
-
VERSION = '0.3.0'
|
10
|
-
|
11
11
|
class << self
|
12
12
|
# Remaps `data` Hash by renaming keys, creating new ones and
|
13
13
|
# optionally aggregating values
|
@@ -22,19 +22,20 @@ class HashRemapper
|
|
22
22
|
#
|
23
23
|
# @param [Hash] data the original Hash to remap
|
24
24
|
# @param [Boolean] pass_trough the flag to pass the original key/value pairs (default: false)
|
25
|
-
# @param [Hash] mapping the Hash which in the simplest case tells how to rename keys
|
25
|
+
# @param [Hash] mapping the Hash which in the simplest case tells how to rename keys (source: :target)
|
26
26
|
#
|
27
27
|
# @return [Hash] remapped version of the original Hash
|
28
28
|
# (selected keys only or all the keys if we passed originals)
|
29
29
|
def remap(data, pass_trough = false, mapping)
|
30
30
|
mapping = pass_trough_mapping(data, mapping) if pass_trough
|
31
31
|
|
32
|
-
mapping.each_with_object({}) do |(from, to), acc|
|
32
|
+
mapping.each_with_object(Hash.new { |hash, key| hash[key] = Hash.new(&hash.default_proc) }) do |(from, to), acc|
|
33
33
|
key, value = try_callable(from, to, data, acc) ||
|
34
|
-
try_digging(to, data) ||
|
34
|
+
try_digging(from, to, data, acc) ||
|
35
|
+
try_nesting(from, to, data, acc) ||
|
35
36
|
[to, data[from]]
|
36
37
|
|
37
|
-
acc
|
38
|
+
set_value(acc, key, value)
|
38
39
|
acc
|
39
40
|
end
|
40
41
|
end
|
@@ -53,11 +54,11 @@ class HashRemapper
|
|
53
54
|
def try_callable(from, to, data, acc)
|
54
55
|
return unless to.respond_to?(:call)
|
55
56
|
|
56
|
-
target_name, target_data = to.call(data
|
57
|
+
target_name, target_data = to.call(data.dig(*from), data)
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
59
|
+
target_data = acc.dig(*target_name).respond_to?(:merge) && target_data.respond_to?(:merge) ?
|
60
|
+
acc.dig(*target_name).merge(target_data) :
|
61
|
+
target_data
|
61
62
|
|
62
63
|
[target_name, target_data]
|
63
64
|
end
|
@@ -66,34 +67,45 @@ class HashRemapper
|
|
66
67
|
# (if the mapping value is enumerable)
|
67
68
|
# @see https://github.com/smileart/hash_digger
|
68
69
|
#
|
69
|
-
# @param [Array] to the target key to map to ([new_key,
|
70
|
+
# @param [Array] to the target key to map to ([new_key, {path: 'a.*.b', strict: false, lambda: ->(res){ … }}])
|
70
71
|
# @param [Hash] data the whole original Hash to use as the digging target
|
71
72
|
#
|
72
73
|
# @return [Array(Object,Object)] key and its value to put to the resulting Hash
|
73
|
-
def try_digging(to, data)
|
74
|
+
def try_digging(_from, to, data, acc)
|
74
75
|
return unless to.respond_to?(:each)
|
75
76
|
|
76
77
|
digger_args = to.fetch(1)
|
77
78
|
|
78
79
|
# v0.1.0 backward compartability layer ([new_key, [:digg, :path, :keys]])
|
79
80
|
return [to.first, data.dig(*to.last)] if digger_args.kind_of?(Array)
|
81
|
+
return unless digger_args.respond_to?(:fetch)
|
80
82
|
|
81
83
|
lambda = digger_args.fetch(:lambda) { nil }
|
82
84
|
|
83
85
|
# @see https://github.com/DamirSvrtan/fasterer — fetch_with_argument_vs_block
|
84
86
|
# @see https://github.com/smileart/hash_digger — digger args
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
87
|
+
target_name = to.fetch(0)
|
88
|
+
|
89
|
+
source_data = HashDigger::Digger.dig(
|
90
|
+
data: data,
|
91
|
+
path: digger_args.fetch(:path) { '*' },
|
92
|
+
strict: digger_args.fetch(:strict) { true },
|
93
|
+
default: digger_args.fetch(:default) { nil },
|
94
|
+
&lambda
|
95
|
+
)
|
96
|
+
|
97
|
+
return [
|
98
|
+
target_name,
|
99
|
+
acc.dig(*target_name).respond_to?(:merge) && source_data.respond_to?(:merge) ?
|
100
|
+
acc.dig(*target_name).merge(source_data) :
|
101
|
+
source_data
|
94
102
|
]
|
95
103
|
end
|
96
104
|
|
105
|
+
def try_nesting(_from, to, data, _acc)
|
106
|
+
[to.fetch(0), data[to.fetch(1)]] if to.respond_to?(:fetch)
|
107
|
+
end
|
108
|
+
|
97
109
|
# Method which automatically prepares direct mapping (e.g. { :a => :a })
|
98
110
|
# for the keys that weren't used in the mapping Hash (to pass them through "as is")
|
99
111
|
#
|
@@ -110,5 +122,16 @@ class HashRemapper
|
|
110
122
|
|
111
123
|
mapping.merge(pass_trough_mapping)
|
112
124
|
end
|
125
|
+
|
126
|
+
def set_value(data, key, value)
|
127
|
+
unless key.respond_to?(:each)
|
128
|
+
data[key] = value
|
129
|
+
return data
|
130
|
+
end
|
131
|
+
|
132
|
+
last_key = key.pop
|
133
|
+
|
134
|
+
data.dig(*key)[last_key] = value
|
135
|
+
end
|
113
136
|
end
|
114
137
|
end
|
metadata
CHANGED
@@ -1,15 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hash_remapper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Serge Bedzhyk
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: hash_digger
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.0'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.0.4
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.0'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.0.4
|
13
33
|
- !ruby/object:Gem::Dependency
|
14
34
|
name: bundler
|
15
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,20 +170,6 @@ dependencies:
|
|
150
170
|
- - "~>"
|
151
171
|
- !ruby/object:Gem::Version
|
152
172
|
version: '0.9'
|
153
|
-
- !ruby/object:Gem::Dependency
|
154
|
-
name: hash_digger
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - "~>"
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: '0.0'
|
160
|
-
type: :runtime
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - "~>"
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: '0.0'
|
167
173
|
description: A little lib which maps original keys to the new ones and more
|
168
174
|
email: smileart21@gmail.com
|
169
175
|
executables: []
|
@@ -182,9 +188,11 @@ files:
|
|
182
188
|
- Rakefile
|
183
189
|
- bin/console
|
184
190
|
- bin/setup
|
191
|
+
- exmaples/weather.rb
|
185
192
|
- hash_remapper.gemspec
|
186
193
|
- img/hash_remapper.png
|
187
194
|
- lib/hash_remapper.rb
|
195
|
+
- lib/hash_remapper/version.rb
|
188
196
|
homepage: https://github.com/smileart/hash_remapper
|
189
197
|
licenses:
|
190
198
|
- MIT
|