map-transform 0.3.12 → 0.4.0-alpha.4
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.
- package/LICENSE +1 -1
- package/README.md +573 -98
- package/dist/functions/compare.d.ts +6 -5
- package/dist/functions/compare.js +21 -16
- package/dist/functions/compare.js.map +1 -1
- package/dist/functions/explode.d.ts +6 -0
- package/dist/functions/explode.js +45 -0
- package/dist/functions/explode.js.map +1 -0
- package/dist/functions/get.d.ts +2 -2
- package/dist/functions/get.js +4 -2
- package/dist/functions/get.js.map +1 -1
- package/dist/functions/index.d.ts +13 -5
- package/dist/functions/index.js +14 -6
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/join.d.ts +2 -2
- package/dist/functions/join.js +1 -1
- package/dist/functions/join.js.map +1 -1
- package/dist/functions/joinSplit.d.ts +8 -0
- package/dist/functions/joinSplit.js +33 -0
- package/dist/functions/joinSplit.js.map +1 -0
- package/dist/functions/logical.d.ts +7 -0
- package/dist/functions/logical.js +28 -0
- package/dist/functions/logical.js.map +1 -0
- package/dist/functions/map.d.ts +8 -0
- package/dist/functions/map.js +43 -0
- package/dist/functions/map.js.map +1 -0
- package/dist/functions/not.d.ts +2 -2
- package/dist/functions/not.js.map +1 -1
- package/dist/functions/template.d.ts +7 -0
- package/dist/functions/template.js +29 -0
- package/dist/functions/template.js.map +1 -0
- package/dist/functions/validate.d.ts +2 -2
- package/dist/functions/validate.js +1 -1
- package/dist/functions/validate.js.map +1 -1
- package/dist/functions/value.d.ts +4 -0
- package/dist/functions/value.js +20 -0
- package/dist/functions/value.js.map +1 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.js +29 -12
- package/dist/index.js.map +1 -1
- package/dist/operations/alt.js +4 -9
- package/dist/operations/alt.js.map +1 -1
- package/dist/operations/apply.d.ts +2 -0
- package/dist/operations/apply.js +15 -0
- package/dist/operations/apply.js.map +1 -0
- package/dist/operations/concat.js +4 -4
- package/dist/operations/concat.js.map +1 -1
- package/dist/operations/directionals.d.ts +1 -1
- package/dist/operations/directionals.js +5 -5
- package/dist/operations/directionals.js.map +1 -1
- package/dist/operations/filter.d.ts +2 -2
- package/dist/operations/filter.js +6 -3
- package/dist/operations/filter.js.map +1 -1
- package/dist/operations/fixed.d.ts +2 -2
- package/dist/operations/fixed.js +5 -1
- package/dist/operations/fixed.js.map +1 -1
- package/dist/operations/getSet.js +17 -24
- package/dist/operations/getSet.js.map +1 -1
- package/dist/operations/ifelse.d.ts +2 -0
- package/dist/operations/ifelse.js +18 -0
- package/dist/operations/ifelse.js.map +1 -0
- package/dist/operations/iterate.d.ts +2 -0
- package/dist/operations/iterate.js +23 -0
- package/dist/operations/iterate.js.map +1 -0
- package/dist/operations/lookup.js +3 -3
- package/dist/operations/lookup.js.map +1 -1
- package/dist/operations/merge.d.ts +2 -0
- package/dist/operations/merge.js +20 -0
- package/dist/operations/merge.js.map +1 -0
- package/dist/operations/modify.d.ts +2 -0
- package/dist/operations/modify.js +18 -0
- package/dist/operations/modify.js.map +1 -0
- package/dist/operations/mutate.js +48 -8
- package/dist/operations/mutate.js.map +1 -1
- package/dist/operations/pipe.js +9 -5
- package/dist/operations/pipe.js.map +1 -1
- package/dist/operations/plug.js +1 -1
- package/dist/operations/plug.js.map +1 -1
- package/dist/operations/root.js +2 -2
- package/dist/operations/root.js.map +1 -1
- package/dist/operations/transform.js +2 -2
- package/dist/operations/transform.js.map +1 -1
- package/dist/operations/value.d.ts +2 -2
- package/dist/operations/value.js +5 -1
- package/dist/operations/value.js.map +1 -1
- package/dist/types.d.ts +46 -21
- package/dist/utils/definitionHelpers.d.ts +7 -8
- package/dist/utils/definitionHelpers.js +60 -43
- package/dist/utils/definitionHelpers.js.map +1 -1
- package/dist/utils/functional.d.ts +2 -0
- package/dist/utils/functional.js +8 -0
- package/dist/utils/functional.js.map +1 -0
- package/dist/utils/is.d.ts +1 -0
- package/dist/utils/is.js +6 -0
- package/dist/utils/is.js.map +1 -0
- package/dist/utils/pathGetter.d.ts +2 -2
- package/dist/utils/pathGetter.js +23 -9
- package/dist/utils/pathGetter.js.map +1 -1
- package/dist/utils/pathSetter.d.ts +3 -3
- package/dist/utils/pathSetter.js +19 -22
- package/dist/utils/pathSetter.js.map +1 -1
- package/dist/utils/stateHelpers.d.ts +7 -36
- package/dist/utils/stateHelpers.js +21 -11
- package/dist/utils/stateHelpers.js.map +1 -1
- package/package.json +17 -54
- package/dist/functions/alt.d.ts +0 -6
- package/dist/functions/alt.js +0 -10
- package/dist/functions/alt.js.map +0 -1
- package/dist/functions/exclude.d.ts +0 -7
- package/dist/functions/exclude.js +0 -16
- package/dist/functions/exclude.js.map +0 -1
- package/dist/utils/objectToMapFunction.d.ts +0 -2
- package/dist/utils/objectToMapFunction.js +0 -34
- package/dist/utils/objectToMapFunction.js.map +0 -1
package/README.md
CHANGED
|
@@ -85,8 +85,8 @@ const def2 = [
|
|
|
85
85
|
{
|
|
86
86
|
title: 'name',
|
|
87
87
|
author: 'meta.author',
|
|
88
|
-
date: 'meta.date'
|
|
89
|
-
}
|
|
88
|
+
date: 'meta.date',
|
|
89
|
+
},
|
|
90
90
|
]
|
|
91
91
|
|
|
92
92
|
const target2 = mapTransform(def2)(source)
|
|
@@ -105,15 +105,15 @@ const { mapTransform, transform } = require('map-transform')
|
|
|
105
105
|
// ....
|
|
106
106
|
|
|
107
107
|
// Write a transform function, that accepts a value and returns a value
|
|
108
|
-
const msToDate = ms => new Date(ms).toISOString()
|
|
108
|
+
const msToDate = (ms) => new Date(ms).toISOString()
|
|
109
109
|
|
|
110
110
|
const def3 = [
|
|
111
111
|
'data[0].content',
|
|
112
112
|
{
|
|
113
113
|
title: 'name',
|
|
114
114
|
author: 'meta.author',
|
|
115
|
-
date: ['meta.date', transform(msToDate)]
|
|
116
|
-
}
|
|
115
|
+
date: ['meta.date', transform(msToDate)],
|
|
116
|
+
},
|
|
117
117
|
]
|
|
118
118
|
|
|
119
119
|
const target3 = mapTransform(def3)(source)
|
|
@@ -143,6 +143,14 @@ Install from npm:
|
|
|
143
143
|
npm install map-transform --save
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
+
## Breaking changes in v0.4
|
|
147
|
+
|
|
148
|
+
- Map objects won't be mapped over an array by default. You have to specify
|
|
149
|
+
`$iterate: true`
|
|
150
|
+
- The `alt` operation now accepts any type of pipeline, but not a helper
|
|
151
|
+
function
|
|
152
|
+
- The root path prefix is changed from `$` to `^`
|
|
153
|
+
|
|
146
154
|
## Usage
|
|
147
155
|
|
|
148
156
|
### The transform object
|
|
@@ -159,15 +167,15 @@ taste.
|
|
|
159
167
|
|
|
160
168
|
```javascript
|
|
161
169
|
const def1 = {
|
|
162
|
-
'data.entry.title': 'heading'
|
|
170
|
+
'data.entry.title': 'heading',
|
|
163
171
|
}
|
|
164
172
|
|
|
165
173
|
const def2 = {
|
|
166
174
|
data: {
|
|
167
175
|
entry: {
|
|
168
|
-
title: 'heading'
|
|
169
|
-
}
|
|
170
|
-
}
|
|
176
|
+
title: 'heading',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
171
179
|
}
|
|
172
180
|
|
|
173
181
|
// def1 and def2 are identical, and will result in an object like this:
|
|
@@ -180,28 +188,55 @@ const def2 = {
|
|
|
180
188
|
// }
|
|
181
189
|
```
|
|
182
190
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
ensure that you get an array, even when the source data contains only an object,
|
|
186
|
-
you may suffix a key with brackets `[]`.
|
|
191
|
+
When you transform an array of data with a mapping object, each item in the
|
|
192
|
+
data array will be be transformed with the mapping object.
|
|
187
193
|
|
|
188
194
|
```javascript
|
|
189
195
|
const def3 = {
|
|
190
|
-
'
|
|
196
|
+
title: 'heading',
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// -->
|
|
200
|
+
// [
|
|
201
|
+
// { title: 'The first heading' },
|
|
202
|
+
// { title: 'The second heading' }
|
|
203
|
+
// ]
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
If you want to apply the mapping object to the entire array – not each item in
|
|
207
|
+
the array – set `$iterate: false` on the mapping object or wrap the object in a
|
|
208
|
+
transform pipeline.
|
|
209
|
+
|
|
210
|
+
**Note:** When a mapping object is part of a transform pipeline, the default is
|
|
211
|
+
to not iterate. See [**transform pipeline**](#transform-pipeline) for more.
|
|
212
|
+
|
|
213
|
+
A key will set whatever is returned by the pipeline (see
|
|
214
|
+
[next section](#values-on-the-transform-object)), whether it is a string, a
|
|
215
|
+
boolean, an array, etc. If you want to ensure that you always get an array, you
|
|
216
|
+
can suffix the key with `[]`. Any value that is not an array will be wrapped in
|
|
217
|
+
one.
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
const def27 = {
|
|
221
|
+
$iterate: false
|
|
222
|
+
'articles[]': {
|
|
191
223
|
title: 'heading'
|
|
192
224
|
}
|
|
193
225
|
}
|
|
194
226
|
|
|
195
|
-
//
|
|
227
|
+
// -->
|
|
196
228
|
// {
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
// ]
|
|
201
|
-
// }
|
|
229
|
+
// articles: [
|
|
230
|
+
// { title: 'Wrapped in an array, even if the data was just an object' },
|
|
231
|
+
// ]
|
|
202
232
|
// }
|
|
203
233
|
```
|
|
204
234
|
|
|
235
|
+
A bonus of using the `[]` suffix, is that when key has another transform object
|
|
236
|
+
as its value, this transform object will be iterated by default (no need to set
|
|
237
|
+
the `$iterate` property). This does not happen to pipelines, paths, or
|
|
238
|
+
operations.
|
|
239
|
+
|
|
205
240
|
#### Values on the transform object
|
|
206
241
|
|
|
207
242
|
The values on the transform objects define how to retrieve and transform data
|
|
@@ -217,7 +252,7 @@ is at this path on the source object.
|
|
|
217
252
|
|
|
218
253
|
```javascript
|
|
219
254
|
const def4 = {
|
|
220
|
-
title: 'data.item.heading'
|
|
255
|
+
title: 'data.item.heading',
|
|
221
256
|
}
|
|
222
257
|
|
|
223
258
|
const source1 = {
|
|
@@ -225,9 +260,9 @@ const source1 = {
|
|
|
225
260
|
item: {
|
|
226
261
|
id: 'item1',
|
|
227
262
|
heading: 'The actual heading',
|
|
228
|
-
intro: 'The actual intro'
|
|
229
|
-
}
|
|
230
|
-
}
|
|
263
|
+
intro: 'The actual intro',
|
|
264
|
+
},
|
|
265
|
+
},
|
|
231
266
|
}
|
|
232
267
|
|
|
233
268
|
// `mapTransform(def4)(source1)` will transform to:
|
|
@@ -251,7 +286,7 @@ an array by indicating the array index in the brackets.
|
|
|
251
286
|
|
|
252
287
|
```javascript
|
|
253
288
|
const def5 = {
|
|
254
|
-
title: 'data.items[0].heading'
|
|
289
|
+
title: 'data.items[0].heading',
|
|
255
290
|
}
|
|
256
291
|
|
|
257
292
|
// def5 will pull the heading from the first item in the `items` array, and will
|
|
@@ -267,6 +302,30 @@ the transform pipeline (which the dot notation path really is, and – come to
|
|
|
267
302
|
think of it – the transform object itself too). This is explained in detail
|
|
268
303
|
below.
|
|
269
304
|
|
|
305
|
+
#### A note on undefined and null
|
|
306
|
+
|
|
307
|
+
MapTransform will treat `undefined` as a value that is not set, and so a
|
|
308
|
+
transform object will not be applied to it. So if an `undefined` value meets
|
|
309
|
+
a transform object, it will produce `undefined`, regardsless of the shape of the
|
|
310
|
+
transform object.
|
|
311
|
+
|
|
312
|
+
This is not the case for `null`, though. MapTransform treats `null` as a value,
|
|
313
|
+
an intended nothing, and will apply a transform object to it, even though it
|
|
314
|
+
will most likely produce nothing but default values. To change this behavior,
|
|
315
|
+
set `mutateNull: false` on the `options` object passed to MapTransform. This
|
|
316
|
+
will essentially make MapTransform treat `null` the same way as `undefined` when
|
|
317
|
+
it comes to the transform object.
|
|
318
|
+
|
|
319
|
+
#### Directional transform objects
|
|
320
|
+
|
|
321
|
+
A transform object is by default applied on forward transformations and in
|
|
322
|
+
reverse. You may alter this by setting the `$direction` prop on a transform
|
|
323
|
+
object, with `fwd`, `rev`, or `both` (the default) as possible values.
|
|
324
|
+
|
|
325
|
+
When running a forward transformation, transform objects marked with
|
|
326
|
+
`$direction: 'rev'` will be skipped. The same goes for `$direction: 'fwd'` in
|
|
327
|
+
reverse.
|
|
328
|
+
|
|
270
329
|
### Transform pipeline
|
|
271
330
|
|
|
272
331
|
The idea of the transform pipeline, is that you describe a set of
|
|
@@ -277,7 +336,7 @@ original format again (although with a potential loss of data, if not all
|
|
|
277
336
|
properties are transformed). This is what you do in a
|
|
278
337
|
[reverse mapping](#reverse-mapping).
|
|
279
338
|
|
|
280
|
-
One way to put it is that the pipeline describes the difference between the two
|
|
339
|
+
One way to put it, is that the pipeline describes the difference between the two
|
|
281
340
|
possible states of the data, and allows you to go back and forth between them.
|
|
282
341
|
Or you can just view it as operations applied in the order they are defined – or
|
|
283
342
|
back again.
|
|
@@ -299,24 +358,33 @@ import { transform, filter } from 'map-transform'
|
|
|
299
358
|
const def6 = [
|
|
300
359
|
'data.items[]',
|
|
301
360
|
{
|
|
361
|
+
$iterate: true,
|
|
302
362
|
id: 'articleNo',
|
|
303
363
|
title: ['headline', transform(maxLength(20))],
|
|
304
|
-
sections: 'meta.sections[].id'
|
|
364
|
+
sections: 'meta.sections[].id',
|
|
305
365
|
},
|
|
306
|
-
filter(onlyItemsWithSection)
|
|
366
|
+
filter(onlyItemsWithSection),
|
|
307
367
|
]
|
|
308
368
|
```
|
|
309
369
|
|
|
310
370
|
(Note that in this example, both `maxLength` and `onlyItemsWithSection` are
|
|
311
371
|
custom functions for this case, but their implementations are not provided.)
|
|
312
372
|
|
|
313
|
-
|
|
373
|
+
**A note on arrays:** In a transform pipeline, the default behavior is to treat
|
|
374
|
+
an array as any other data. The array will be passed on to a `transform()`
|
|
375
|
+
operation, the entire array will be set on a path, etc. This also means that a
|
|
376
|
+
mapping object will be applied to the entire array if nothing else is specified.
|
|
377
|
+
In the example above, we have set `$iterate: true` on the mapping object, to
|
|
378
|
+
signal that we want the mapping to be applied to the items of any array. See
|
|
379
|
+
also [the `iterate` operation](#iteratepipeline-operation) for more.
|
|
380
|
+
|
|
381
|
+
#### `transform(transformFn, transformFnRev)` operation
|
|
314
382
|
|
|
315
383
|
The simple beauty of the `transform()` operation, is that it will apply whatever
|
|
316
384
|
function you provide it with to the data at that point in the pipeline. It's
|
|
317
385
|
completely up to you to write the function that does the transformation.
|
|
318
386
|
|
|
319
|
-
You may supply a second function (`
|
|
387
|
+
You may supply a second function (`transformFnRev`), that will be used when
|
|
320
388
|
[reverse mapping](#reverse-mapping). If you only supplies one function, it will
|
|
321
389
|
be used in both directions. You may supply `null` for either of these, to make
|
|
322
390
|
it uni-directional, but it might be clearer to use `fwd()` or `rev()` operations
|
|
@@ -336,16 +404,16 @@ on an object. You would probably just not get the end result you expected.
|
|
|
336
404
|
```javascript
|
|
337
405
|
import { mapTransform, transform } from 'map-transform'
|
|
338
406
|
|
|
339
|
-
const ensureInteger = data => Number.parseInt(data, 10) || 0
|
|
407
|
+
const ensureInteger = (data) => Number.parseInt(data, 10) || 0
|
|
340
408
|
const def7 = {
|
|
341
|
-
count: ['statistics.views', transform(ensureInteger)]
|
|
409
|
+
count: ['statistics.views', transform(ensureInteger)],
|
|
342
410
|
}
|
|
343
411
|
|
|
344
412
|
const data = {
|
|
345
413
|
statistics: {
|
|
346
|
-
view: '18'
|
|
414
|
+
view: '18',
|
|
347
415
|
// ...
|
|
348
|
-
}
|
|
416
|
+
},
|
|
349
417
|
}
|
|
350
418
|
|
|
351
419
|
mapTransform(def7)(data)
|
|
@@ -381,41 +449,50 @@ Example transformation pipeline with a `yearsSince` function:
|
|
|
381
449
|
|
|
382
450
|
```javascript
|
|
383
451
|
const def8 = {
|
|
384
|
-
age: ['birthyear', yearsSince(new Date())]
|
|
452
|
+
age: ['birthyear', yearsSince(new Date())],
|
|
385
453
|
}
|
|
386
454
|
```
|
|
387
455
|
|
|
456
|
+
**Note:** When the `transform` operation is applied to an array, it will not
|
|
457
|
+
iterate the array. Mapping over each item needs to be handled in the transform
|
|
458
|
+
itself, or wrap the `transform` operation in an `iterate` operation.
|
|
459
|
+
|
|
388
460
|
You may also define a transform operation as an object:
|
|
389
461
|
|
|
390
462
|
```javascript
|
|
391
463
|
import { mapTransform } from 'map-transform'
|
|
392
464
|
|
|
393
|
-
const ensureInteger = operands => data => Number.parseInt(data, 10) || 0
|
|
394
|
-
const
|
|
465
|
+
const ensureInteger = (operands) => (data) => Number.parseInt(data, 10) || 0
|
|
466
|
+
const functions = { ensureInteger }
|
|
395
467
|
const def7asObject = {
|
|
396
|
-
count: ['statistics.views', { $transform: 'ensureInteger' }]
|
|
468
|
+
count: ['statistics.views', { $transform: 'ensureInteger' }],
|
|
397
469
|
}
|
|
398
470
|
|
|
399
471
|
const data = {
|
|
400
472
|
statistics: {
|
|
401
|
-
view: '18'
|
|
473
|
+
view: '18',
|
|
402
474
|
// ...
|
|
403
|
-
}
|
|
475
|
+
},
|
|
404
476
|
}
|
|
405
477
|
|
|
406
|
-
mapTransform(def7asObject, {
|
|
478
|
+
mapTransform(def7asObject, { functions })(data)
|
|
407
479
|
// --> {
|
|
408
480
|
// count: 18
|
|
409
481
|
// }
|
|
410
482
|
```
|
|
411
483
|
|
|
412
|
-
Note that the function itself is passed on the `
|
|
484
|
+
Note that the function itself is passed on the `functions` object. When
|
|
413
485
|
you provide the custom function this way, it should be given as a function
|
|
414
486
|
accepting an object with operands / arguments, that returns the actual function
|
|
415
487
|
used in the transform. Any properties given on the operation object, apart from
|
|
416
488
|
`$transform`, will be passed in the `operands` object.
|
|
417
489
|
|
|
418
|
-
|
|
490
|
+
When you define the `transform` operation as an object, you may specify
|
|
491
|
+
`$iterate: true` on the object to apply the transform to every item on an array,
|
|
492
|
+
if an array is encountered. You may also set `$direction: 'fwd'` or
|
|
493
|
+
`$direction: 'rev'` to have it transform in one direction only.
|
|
494
|
+
|
|
495
|
+
#### `filter(conditionFn)` operation
|
|
419
496
|
|
|
420
497
|
Just like the transform operation, the filter operation will apply whatever
|
|
421
498
|
function you give it to the data at that point in the transform pipeline, but
|
|
@@ -458,7 +535,7 @@ Defining a filter operation as an object:
|
|
|
458
535
|
import { mapTransform } from 'map-transform'
|
|
459
536
|
|
|
460
537
|
const onlyActives = (data) => data.active
|
|
461
|
-
const
|
|
538
|
+
const functions = { onlyActives }
|
|
462
539
|
const def9asObject = [
|
|
463
540
|
'members'
|
|
464
541
|
{
|
|
@@ -469,7 +546,120 @@ const def9asObject = [
|
|
|
469
546
|
]
|
|
470
547
|
```
|
|
471
548
|
|
|
472
|
-
|
|
549
|
+
You may also set `$direction: 'fwd'` or `$direction: 'rev'` on the object, to
|
|
550
|
+
have it filter in one direction only.
|
|
551
|
+
|
|
552
|
+
See the `transform()` operation for more on how defining as an object works.
|
|
553
|
+
|
|
554
|
+
#### `ifelse(conditionFn, truePipeline, falsePipeline)` operation
|
|
555
|
+
|
|
556
|
+
The `ifelse()` operation will run the `truePipeline` if the `conditionFn` results
|
|
557
|
+
in `true`, otherwise it will run the `falsePipeline`. See
|
|
558
|
+
[the `filter()` operation](#filterconditionFn-operation) for more on the
|
|
559
|
+
requirements for the `conditionFn`.
|
|
560
|
+
|
|
561
|
+
Both `truePipeline` and `falsePipeline` are optional, in case you only need to
|
|
562
|
+
apply a pipeline in one of the cases.
|
|
563
|
+
|
|
564
|
+
Example:
|
|
565
|
+
|
|
566
|
+
```javascript
|
|
567
|
+
import { mapTransform, ifelse } from 'map-transform'
|
|
568
|
+
|
|
569
|
+
const onlyActives = (data) => data.active
|
|
570
|
+
const def31 = [
|
|
571
|
+
'members'
|
|
572
|
+
{
|
|
573
|
+
name: 'name',
|
|
574
|
+
active: 'hasPayed'
|
|
575
|
+
},
|
|
576
|
+
ifelse(onlyActives, set('active[]'), set('inactive[]'))
|
|
577
|
+
]
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
#### `iterate(pipeline)` operation
|
|
581
|
+
|
|
582
|
+
If you want something to be mapped over the items of an array, the `iterate`
|
|
583
|
+
operation is your friend. When you wrap another operation, a pipeline, or a
|
|
584
|
+
mapping object in an `iterate` operation, it will be applied to each item.
|
|
585
|
+
|
|
586
|
+
When iterating, the state's `context` will iterate as well, to support `alt`
|
|
587
|
+
operations etc. that need to access the corresponding item in the context.
|
|
588
|
+
|
|
589
|
+
In this example, each value in the array returned by `statistics[].views` will
|
|
590
|
+
be transformed with the `ensureInteger` function, even if the function does not
|
|
591
|
+
support arrays:
|
|
592
|
+
|
|
593
|
+
```javascript
|
|
594
|
+
import { mapTransform, iterate } from 'map-transform'
|
|
595
|
+
|
|
596
|
+
const ensureInteger = data => Number.parseInt(data, 10) || 0
|
|
597
|
+
const def26 = [
|
|
598
|
+
counts: ['statistics[].views', iterate(transform(ensureInteger))]
|
|
599
|
+
]
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
The `iterate` operation may also be used to wrap mapping objects in a transform
|
|
603
|
+
pipeline, however, setting `$iterate: true` on the object may be more
|
|
604
|
+
convenient.
|
|
605
|
+
|
|
606
|
+
#### `apply(pipelineId)` operation
|
|
607
|
+
|
|
608
|
+
The apply operation allows pipelines to be reused. Several pipelines may be
|
|
609
|
+
provided on the `pipelines` object on the `options` provided to
|
|
610
|
+
`mapTransform()`, and the keys of the `pipelines` object serves as ids.
|
|
611
|
+
When an id is passed to the apply operation as `pipelinedId`, the pipeline will
|
|
612
|
+
be applied in the place of the apply operation and executed as if it was part of
|
|
613
|
+
the pipeline definition in the first place.
|
|
614
|
+
|
|
615
|
+
```javascript
|
|
616
|
+
import { mapTransform, apply, transform } from 'map-transform'
|
|
617
|
+
|
|
618
|
+
const ensureInteger = (data) => Number.parseInt(data, 10) || 0
|
|
619
|
+
const pipelines = {
|
|
620
|
+
castEntry: {
|
|
621
|
+
title: ['title', transform(String)],
|
|
622
|
+
count: ['count', transform(ensureInteger)],
|
|
623
|
+
},
|
|
624
|
+
}
|
|
625
|
+
const def25 = [
|
|
626
|
+
{
|
|
627
|
+
title: 'heading',
|
|
628
|
+
count: 'statistics.views',
|
|
629
|
+
},
|
|
630
|
+
apply('castEntry'),
|
|
631
|
+
]
|
|
632
|
+
|
|
633
|
+
const data = {
|
|
634
|
+
heading: 'Entry 1',
|
|
635
|
+
statistics: {
|
|
636
|
+
view: '18',
|
|
637
|
+
},
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
mapTransform(def7)(data)
|
|
641
|
+
// --> {
|
|
642
|
+
// title: 'Entry 1',
|
|
643
|
+
// count: 18
|
|
644
|
+
// }
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
You may also define the apply operation as an operation object:
|
|
648
|
+
|
|
649
|
+
```javascript
|
|
650
|
+
const def25 = [
|
|
651
|
+
{
|
|
652
|
+
title: 'heading',
|
|
653
|
+
count: 'statistics.views',
|
|
654
|
+
},
|
|
655
|
+
{ $apply: 'castEntry' },
|
|
656
|
+
]
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
When you define the `apply` operation as an object, you may specify
|
|
660
|
+
`$iterate: true` on the object to apply the pipeline to every item on an array,
|
|
661
|
+
if an array is encountered. You may also set `$direction: 'fwd'` or
|
|
662
|
+
`$direction: 'rev'` to have it apply in one direction only.
|
|
473
663
|
|
|
474
664
|
#### `value(data)` operation
|
|
475
665
|
|
|
@@ -490,12 +680,23 @@ import { value, alt } from 'map-transform'
|
|
|
490
680
|
const def10 = {
|
|
491
681
|
id: 'data.customerNo',
|
|
492
682
|
type: value('customer'),
|
|
493
|
-
name: ['data.name', alt(value('Anonymous'))]
|
|
683
|
+
name: ['data.name', alt(value('Anonymous'))],
|
|
494
684
|
}
|
|
495
685
|
```
|
|
496
686
|
|
|
497
687
|
The operation will not set anything when mapping with `.onlyMappedValues()`.
|
|
498
688
|
|
|
689
|
+
If you want to define the `value` operation as an operation object, use
|
|
690
|
+
`$transform` or `$alt`:
|
|
691
|
+
|
|
692
|
+
```javascript
|
|
693
|
+
const def10asObject = {
|
|
694
|
+
id: 'data.customerNo',
|
|
695
|
+
type: { $transform: 'value', value: 'customer' },
|
|
696
|
+
name: ['data.name', { $alt: 'value', value: 'Anonymous' }],
|
|
697
|
+
}
|
|
698
|
+
```
|
|
699
|
+
|
|
499
700
|
#### `fixed(data)` operation
|
|
500
701
|
|
|
501
702
|
The data given to the fixed operation, will be inserted in the pipeline in place
|
|
@@ -507,15 +708,20 @@ will be included when mapping with `.onlyMappedValues()` as well.
|
|
|
507
708
|
|
|
508
709
|
#### `alt(pipeline)` operation
|
|
509
710
|
|
|
510
|
-
The alt operation will apply the
|
|
511
|
-
|
|
512
|
-
|
|
711
|
+
The alt operation will apply the pipeline it is given when the data already in
|
|
712
|
+
the pipeline is `undefined`. This is how you provide default values in
|
|
713
|
+
MapTransform. The pipeline may be as simple as a `value()` operation, a dot
|
|
513
714
|
notation path into the source data, or a full pipeline of several operations.
|
|
514
715
|
|
|
716
|
+
When given an array, the `alt` operation will treat it as a value and do
|
|
717
|
+
nothing, as it is not an `undefined` value. To provide the `alt` operation to
|
|
718
|
+
every item in the array, please use the `iterate` operation.
|
|
719
|
+
|
|
515
720
|
```javascript
|
|
516
|
-
import { alt, transform,
|
|
517
|
-
const
|
|
518
|
-
const
|
|
721
|
+
import { alt, transform, functions } from 'map-transform'
|
|
722
|
+
const { value } = functions
|
|
723
|
+
const currentDate = (data) => new Date()
|
|
724
|
+
const formatDate = (data) => {
|
|
519
725
|
/* implementation not included */
|
|
520
726
|
}
|
|
521
727
|
|
|
@@ -526,30 +732,38 @@ const def11 = {
|
|
|
526
732
|
'data.updateDate',
|
|
527
733
|
alt('data.createDate'),
|
|
528
734
|
alt(transform(currentDate)),
|
|
529
|
-
transform(formatDate)
|
|
530
|
-
]
|
|
735
|
+
transform(formatDate),
|
|
736
|
+
],
|
|
531
737
|
}
|
|
532
738
|
```
|
|
533
739
|
|
|
534
740
|
In the example above, we first try to set the `updatedAt` prop to the data found
|
|
535
741
|
at `data.updateDate` in the source data. If that does not exist (i.e. we get
|
|
536
742
|
`undefined`), the alt operation kicks in and try the path `data.createDate`. If
|
|
537
|
-
we still have `undefined`, the second alt will call
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
743
|
+
we still have `undefined`, the second alt will call the customer function
|
|
744
|
+
`currentDate`, that simply returns the current date as a JS object. Finally,
|
|
745
|
+
another transform operation pipes whatever data we get from all of this through
|
|
746
|
+
the `formatDate` function.
|
|
541
747
|
|
|
542
748
|
You may also define an alt operation as an object:
|
|
543
749
|
|
|
544
750
|
```javascript
|
|
545
751
|
const def11asObject = {
|
|
546
752
|
id: 'data.id',
|
|
547
|
-
name: ['data.name', { $
|
|
753
|
+
name: ['data.name', { $alt: 'value', value: 'Anonymous' }],
|
|
754
|
+
updatedAt: [
|
|
755
|
+
'data.updateDate',
|
|
756
|
+
{ $alt: 'get', path: 'data.createDate' },
|
|
757
|
+
{ $alt: 'currentDate' },
|
|
758
|
+
{ $transform: 'formatDate' },
|
|
759
|
+
],
|
|
548
760
|
}
|
|
549
761
|
```
|
|
550
762
|
|
|
551
|
-
|
|
552
|
-
`
|
|
763
|
+
When you define the `alt` operation as an object, you may specify
|
|
764
|
+
`$iterate: true` on the object to provide a default value to every `undefined`
|
|
765
|
+
item on an array, if an array is encountered. You may also set
|
|
766
|
+
`$direction: 'fwd'` or `$direction: 'rev'` to limit it to one direction only.
|
|
553
767
|
|
|
554
768
|
#### `concat(pipeline, pipeline, ...)` operation
|
|
555
769
|
|
|
@@ -561,6 +775,49 @@ This operation will always return an array, even when it is given only one
|
|
|
561
775
|
pipeline that does not return an array. Pipelines that does not result in a
|
|
562
776
|
value (i.e. return `undefined`) will be filtered away.
|
|
563
777
|
|
|
778
|
+
#### `merge(pipeline, pipeline, ...)` operation
|
|
779
|
+
|
|
780
|
+
`merge()` will run all given pipelines and deep merge their results. Conflicts
|
|
781
|
+
are resolved by prioritizing results from the rightmost of the conflicting
|
|
782
|
+
pipelines.
|
|
783
|
+
|
|
784
|
+
#### `modify(pipeline)` operation
|
|
785
|
+
|
|
786
|
+
Use the `modify()` operation when you want the pipeline to modify an object,
|
|
787
|
+
instead of replacing it.
|
|
788
|
+
|
|
789
|
+
Example:
|
|
790
|
+
|
|
791
|
+
```javascript
|
|
792
|
+
import { modify } from 'map-transform'
|
|
793
|
+
|
|
794
|
+
const def34 = modify({
|
|
795
|
+
data: 'data.deeply.placed.items',
|
|
796
|
+
})
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
`def34` will in effect set the values placed at a deep path on the `data`
|
|
800
|
+
prop. Giving this an object like:
|
|
801
|
+
|
|
802
|
+
```javascript
|
|
803
|
+
const response = {
|
|
804
|
+
status: 'ok',
|
|
805
|
+
data: { deeply: { placed: { items: [{ id: 'ent1' }] } } },
|
|
806
|
+
}
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
...will result in:
|
|
810
|
+
|
|
811
|
+
```javascript
|
|
812
|
+
const response = {
|
|
813
|
+
status: 'ok',
|
|
814
|
+
data: [{ id: 'ent1' }],
|
|
815
|
+
}
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
Had we ran this without the `modify()` operation, the returned object would only
|
|
819
|
+
have the `data` prop.
|
|
820
|
+
|
|
564
821
|
#### `fwd(pipeline)` and `rev(pipeline)` operation
|
|
565
822
|
|
|
566
823
|
All operations in MapTransform will apply in both directions, although some of
|
|
@@ -573,11 +830,11 @@ pipeline when we're mapping in reverse.
|
|
|
573
830
|
|
|
574
831
|
```javascript
|
|
575
832
|
import { fwd, rev, transform } from 'map-transform'
|
|
576
|
-
const increment = data => data + 1
|
|
577
|
-
const decrement = data => data - 1
|
|
833
|
+
const increment = (data) => data + 1
|
|
834
|
+
const decrement = (data) => data - 1
|
|
578
835
|
|
|
579
836
|
const def12 = {
|
|
580
|
-
order: ['index', fwd(transform(increment)), rev(transform(decrement))]
|
|
837
|
+
order: ['index', fwd(transform(increment)), rev(transform(decrement))],
|
|
581
838
|
}
|
|
582
839
|
```
|
|
583
840
|
|
|
@@ -630,7 +887,7 @@ This example results in the exact same pipeline as the example above:
|
|
|
630
887
|
|
|
631
888
|
```javascript
|
|
632
889
|
const def14 = {
|
|
633
|
-
'content[]': 'data.items[].content'
|
|
890
|
+
'content[]': 'data.items[].content',
|
|
634
891
|
}
|
|
635
892
|
```
|
|
636
893
|
|
|
@@ -659,13 +916,13 @@ const def15 = [
|
|
|
659
916
|
{
|
|
660
917
|
id: 'id',
|
|
661
918
|
title: 'headline',
|
|
662
|
-
section: root('meta.section')
|
|
663
|
-
}
|
|
919
|
+
section: root('meta.section'),
|
|
920
|
+
},
|
|
664
921
|
]
|
|
665
922
|
|
|
666
923
|
const data = {
|
|
667
924
|
articles: [{ id: '1', headline: 'An article' } /* ... */],
|
|
668
|
-
meta: { section: 'news' }
|
|
925
|
+
meta: { section: 'news' },
|
|
669
926
|
}
|
|
670
927
|
|
|
671
928
|
mapTransform(def15)(data)
|
|
@@ -680,14 +937,14 @@ As you see, every item in the `articles[]` array, will be mapped with the
|
|
|
680
937
|
items without the root operation.
|
|
681
938
|
|
|
682
939
|
There's also a shortcut notation for root, by prefixing a dot notation path with
|
|
683
|
-
|
|
940
|
+
`^`. This only works when the path is used for getting a value, and it will be
|
|
684
941
|
plugged when used as set (i.e., it will return no value). This may be used in
|
|
685
942
|
`get()` and `set()` operations, and in transformation objects.
|
|
686
943
|
|
|
687
944
|
In the following example, `def16` and `def17` is exactly the same:
|
|
688
945
|
|
|
689
946
|
```javascript
|
|
690
|
-
const def16 = get('
|
|
947
|
+
const def16 = get('^meta.section')
|
|
691
948
|
const def17 = divide(root('meta.section'), plug())
|
|
692
949
|
```
|
|
693
950
|
|
|
@@ -727,8 +984,8 @@ const data = {
|
|
|
727
984
|
users: [
|
|
728
985
|
{ id: 'user1', name: 'User 1' },
|
|
729
986
|
{ id: 'user2', name: 'User 2' },
|
|
730
|
-
{ id: 'user3', name: 'User 3' }
|
|
731
|
-
]
|
|
987
|
+
{ id: 'user3', name: 'User 3' },
|
|
988
|
+
],
|
|
732
989
|
}
|
|
733
990
|
const mapper = mapTransform(def18)
|
|
734
991
|
const mappedData = mapper(data)
|
|
@@ -741,31 +998,193 @@ mapper.rev(mappedData)
|
|
|
741
998
|
// --> { content: { meta: { authors: ['user1', 'user3'] } } }
|
|
742
999
|
```
|
|
743
1000
|
|
|
744
|
-
#### `compare(path,
|
|
1001
|
+
#### `compare({ path, operator, match, matchPath })` function
|
|
1002
|
+
|
|
1003
|
+
This is a helper function intended for use with the `filter()` operation. You
|
|
1004
|
+
pass a dot notation `path` and a `match` value (string, number, boolean) to
|
|
1005
|
+
`compare()`, and it returns a function that you can pass to `filter()` for
|
|
1006
|
+
filtering away data that does not not have the value set at the provided path.
|
|
1007
|
+
|
|
1008
|
+
As an alternative to `match`, you may specify a `matchPath`, which is a dot
|
|
1009
|
+
notation path, in which case the match value will be fetched from the provided
|
|
1010
|
+
data.
|
|
745
1011
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
array, the value is expected to be one of the values in
|
|
1012
|
+
The default is to compare the values resulting from `path` and `match` or
|
|
1013
|
+
`matchPath` with equality, but other operations may be set on the `operator`
|
|
1014
|
+
property. Alternatives: `'='`, `'!='`, `'>'`, `'>='`, `'<'`, or `'<='`.
|
|
1015
|
+
|
|
1016
|
+
If the path points to an array, the value is expected to be one of the values in
|
|
1017
|
+
the array.
|
|
751
1018
|
|
|
752
1019
|
Here's an example where only data where role is set to 'admin' will be kept:
|
|
753
1020
|
|
|
754
1021
|
```javascript
|
|
755
|
-
import { filter,
|
|
1022
|
+
import { filter, functions } from 'map-transform'
|
|
1023
|
+
const { compare } = functions
|
|
756
1024
|
|
|
757
1025
|
const def19 = [
|
|
758
1026
|
{
|
|
759
1027
|
name: 'name',
|
|
760
|
-
role: 'editor'
|
|
1028
|
+
role: 'editor',
|
|
1029
|
+
},
|
|
1030
|
+
filter(compare({ path: 'role', operator: '=', match: 'admin' })),
|
|
1031
|
+
]
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
You may also define this with a transform object:
|
|
1035
|
+
|
|
1036
|
+
```javascript
|
|
1037
|
+
const def19o = [
|
|
1038
|
+
{
|
|
1039
|
+
name: 'name',
|
|
1040
|
+
role: 'editor',
|
|
761
1041
|
},
|
|
762
|
-
filter
|
|
1042
|
+
{ $filter: 'compare', path: 'role', operator: '=', match: 'admin' },
|
|
763
1043
|
]
|
|
764
1044
|
```
|
|
765
1045
|
|
|
766
|
-
#### `
|
|
1046
|
+
#### `explode()` function
|
|
1047
|
+
|
|
1048
|
+
Given an object, the `explode` helper function will return an array with one
|
|
1049
|
+
object for each property in the source object, with a `key` property for the
|
|
1050
|
+
property key, and a `value` property for the value.
|
|
1051
|
+
|
|
1052
|
+
When given an array, the `explode` helper will return on object for every item
|
|
1053
|
+
in the array, with a `key` property set to the index number in the source array
|
|
1054
|
+
and a `value` property to the item value.
|
|
1055
|
+
|
|
1056
|
+
When transforming in reverse, `explode` will try to compile an object or an
|
|
1057
|
+
array from an array of key/value objects. If all `key` props are numbers, an
|
|
1058
|
+
array is produced, otherwise an object. Anything that don't match the expected
|
|
1059
|
+
structure will be skipped.
|
|
1060
|
+
|
|
1061
|
+
Example:
|
|
1062
|
+
|
|
1063
|
+
```javascript
|
|
1064
|
+
import { mapTransform, transform, functions } from 'map-transform'
|
|
1065
|
+
const { explode } = functions
|
|
1066
|
+
|
|
1067
|
+
const data = {
|
|
1068
|
+
currencies: { NOK: 1, USD: 0.125, EUR: 0.1 },
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
const def32 = ['currencies', transform(explode())]
|
|
1072
|
+
|
|
1073
|
+
mapTransform(def32)(data)
|
|
1074
|
+
// --> [{ key: 'NOK', value: 1 }, { key: 'USD', value: 0.125 },
|
|
1075
|
+
// { key: 'EUR', value: 0.1 }]
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
Or as a transform object:
|
|
1079
|
+
|
|
1080
|
+
```javascript
|
|
1081
|
+
const def32o = ['currencies', { $transform: 'explode' }]
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
#### `map(dictionary)` function
|
|
1085
|
+
|
|
1086
|
+
This helper function accepts a dictionary described as an array of tuples, where
|
|
1087
|
+
each tuple holds a from value and a to value. When a data value is given to the
|
|
1088
|
+
`map` helper, it is replaced with a value from the dictionary. For a forward
|
|
1089
|
+
transformation, the first value in the tuple will be matched with the given
|
|
1090
|
+
data value, and the second value will be returned. In reverse transformation,
|
|
1091
|
+
the second value is matched and the first is returned.
|
|
1092
|
+
|
|
1093
|
+
The wildcard value `*` will match any value, and is applied if there is no other
|
|
1094
|
+
match in the dictionary. When the returned value is `*`, the original data value
|
|
1095
|
+
is returned instead.
|
|
1096
|
+
|
|
1097
|
+
The `map` function only supports primitive values, so any object will be mapped to
|
|
1098
|
+
`undefined` or the value given by the wildcard in the dictionary. Arrays will be
|
|
1099
|
+
iterated to map each value in the array.
|
|
1100
|
+
|
|
1101
|
+
Example:
|
|
1102
|
+
|
|
1103
|
+
```
|
|
1104
|
+
import { transform, functions } from 'map-transform'
|
|
1105
|
+
const { map } = functions
|
|
1106
|
+
|
|
1107
|
+
const dictionary = [
|
|
1108
|
+
[200, 'ok'],
|
|
1109
|
+
[404, 'notfound'],
|
|
1110
|
+
['*', 'error']
|
|
1111
|
+
]
|
|
1112
|
+
|
|
1113
|
+
const def28 = {
|
|
1114
|
+
status: ['result', transform(map({ dictionary }))]
|
|
1115
|
+
}
|
|
1116
|
+
```
|
|
767
1117
|
|
|
768
|
-
|
|
1118
|
+
When using `map` in an operation object, you may provice a dictionary array
|
|
1119
|
+
or a named dictionary on the `dictionary` property. An example of with a named
|
|
1120
|
+
dictionary:
|
|
1121
|
+
|
|
1122
|
+
```
|
|
1123
|
+
import { mapTransform } from 'map-transform'
|
|
1124
|
+
|
|
1125
|
+
const dictionary = [
|
|
1126
|
+
[200, 'ok'],
|
|
1127
|
+
[404, 'notfound'],
|
|
1128
|
+
['*', 'error']
|
|
1129
|
+
]
|
|
1130
|
+
|
|
1131
|
+
const def29 = {
|
|
1132
|
+
status: [
|
|
1133
|
+
'result',
|
|
1134
|
+
{ $transform: 'map', dictionary: 'statusCodes' }
|
|
1135
|
+
]
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const mapper = mapTransform(def29, { dictionaries: { statusCodes: dictionary } })
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
#### `template(template)` function
|
|
1142
|
+
|
|
1143
|
+
The `template` helper function takes a [handlebars] template and applies the
|
|
1144
|
+
given data to it. The placeholders in the template is dot notaion paths to
|
|
1145
|
+
fields in the given data. A simple dot (`.`) refers to the data itself, and may
|
|
1146
|
+
be useful when the pipeline data is a string.
|
|
1147
|
+
|
|
1148
|
+
Values will be forced to strings before being inserted in the template.
|
|
1149
|
+
|
|
1150
|
+
Example:
|
|
1151
|
+
|
|
1152
|
+
```javascript
|
|
1153
|
+
import { mapTransform, transform, functions } from 'map-transform'
|
|
1154
|
+
const { template } = functions
|
|
1155
|
+
|
|
1156
|
+
const data = {
|
|
1157
|
+
content: { description: 'Bergen by night', artist: 'John F.' },
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
const def30 = {
|
|
1161
|
+
caption: ['content', transform(template('{{description}}. By {{artist}}'))],
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
const ret = mapTransform(def30)(data)
|
|
1165
|
+
// --> { caption: 'Bergen by night. By John F.' }
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
The `template` function is also available through a transform object:
|
|
1169
|
+
|
|
1170
|
+
```javascript
|
|
1171
|
+
const def30o = {
|
|
1172
|
+
caption: [
|
|
1173
|
+
'content',
|
|
1174
|
+
{ $transform: 'template', template: '{{description}}. By {{artist}}' },
|
|
1175
|
+
],
|
|
1176
|
+
}
|
|
1177
|
+
```
|
|
1178
|
+
|
|
1179
|
+
If you the template is available as part of the data, you may use `templatePath`
|
|
1180
|
+
instead of `template`, and set it to the path of the template in the data. When
|
|
1181
|
+
using the transform object format, this is as simple as it sounds. With the
|
|
1182
|
+
function version, you supply an operand object to `template()` like this:
|
|
1183
|
+
`transform(template({ templatePath: 'options.captionTemplate' }))`.
|
|
1184
|
+
|
|
1185
|
+
#### `validate(path, schema)` function
|
|
1186
|
+
|
|
1187
|
+
This is a helper function for validating the value at the path against a
|
|
769
1188
|
[JSON Schema](http://json-schema.org). We won't go into details of JSON Schema
|
|
770
1189
|
here, and the `validate()` helper simply retrieves the value at the path and
|
|
771
1190
|
validates it according to the provided schema.
|
|
@@ -774,18 +1193,19 @@ Note that if you provide a schema that is always valid, it will be valid even
|
|
|
774
1193
|
when the data has no value at the given path.
|
|
775
1194
|
|
|
776
1195
|
```javascript
|
|
777
|
-
import { filter,
|
|
1196
|
+
import { filter, functions } from 'map-transform'
|
|
1197
|
+
const { validate } = functions
|
|
778
1198
|
|
|
779
1199
|
const def20 = [
|
|
780
1200
|
'items',
|
|
781
1201
|
filter(validate('draft', { const: false })),
|
|
782
1202
|
{
|
|
783
|
-
title: 'heading'
|
|
784
|
-
}
|
|
1203
|
+
title: 'heading',
|
|
1204
|
+
},
|
|
785
1205
|
]
|
|
786
1206
|
```
|
|
787
1207
|
|
|
788
|
-
#### `not(value)`
|
|
1208
|
+
#### `not(value)` function
|
|
789
1209
|
|
|
790
1210
|
`not()` will return `false` when value if truthy and `true` when value is falsy.
|
|
791
1211
|
This is useful for making the `filter()` operation do the opposite of what the
|
|
@@ -794,14 +1214,15 @@ filter function implies.
|
|
|
794
1214
|
Here we filter away all data where role is set to 'admin':
|
|
795
1215
|
|
|
796
1216
|
```javascript
|
|
797
|
-
import { filter,
|
|
1217
|
+
import { filter, functions } from 'map-transform'
|
|
1218
|
+
const { compare } = functions
|
|
798
1219
|
|
|
799
1220
|
const def21 = [
|
|
800
1221
|
{
|
|
801
1222
|
name: 'name',
|
|
802
|
-
role: 'role'
|
|
1223
|
+
role: 'role',
|
|
803
1224
|
},
|
|
804
|
-
filter(not(compare('role', 'admin')))
|
|
1225
|
+
filter(not(compare('role', 'admin'))),
|
|
805
1226
|
]
|
|
806
1227
|
```
|
|
807
1228
|
|
|
@@ -828,14 +1249,14 @@ const def22 = [
|
|
|
828
1249
|
'data.customers[]',
|
|
829
1250
|
{
|
|
830
1251
|
id: 'customerNo',
|
|
831
|
-
name: ['fullname', alt(value('Anonymous'))]
|
|
832
|
-
}
|
|
1252
|
+
name: ['fullname', alt(value('Anonymous'))],
|
|
1253
|
+
},
|
|
833
1254
|
]
|
|
834
1255
|
|
|
835
1256
|
const dataInTargetState = [
|
|
836
1257
|
{ id: 'cust1', name: 'Fred Johnsen' },
|
|
837
|
-
{ id: 'cust2', name: 'Lucy Knight' },
|
|
838
|
-
{ id: 'cust3' }
|
|
1258
|
+
// { id: 'cust2', name: 'Lucy Knight' },
|
|
1259
|
+
{ id: 'cust3' },
|
|
839
1260
|
]
|
|
840
1261
|
|
|
841
1262
|
const dataInSourceState = mapTransform(def22).rev(dataInTargetState)
|
|
@@ -861,15 +1282,15 @@ For example:
|
|
|
861
1282
|
```javascript
|
|
862
1283
|
import { mapTransform, transform } from 'map-transform'
|
|
863
1284
|
|
|
864
|
-
const username = name => name.replace(/\s+/, '.').toLowerCase()
|
|
1285
|
+
const username = (name) => name.replace(/\s+/, '.').toLowerCase()
|
|
865
1286
|
|
|
866
1287
|
const def23 = [
|
|
867
1288
|
'data.customers[]',
|
|
868
1289
|
{
|
|
869
1290
|
id: 'customerNo',
|
|
870
1291
|
name: 'fullname',
|
|
871
|
-
'name/1': ['username', rev(transform(username))]
|
|
872
|
-
}
|
|
1292
|
+
'name/1': ['username', rev(transform(username))],
|
|
1293
|
+
},
|
|
873
1294
|
]
|
|
874
1295
|
|
|
875
1296
|
const dataInTargetState = [{ id: 'cust1', name: 'Fred Johnsen' }]
|
|
@@ -884,6 +1305,60 @@ const dataInSourceState = mapTransform(def23).rev(dataInTargetState)
|
|
|
884
1305
|
// }
|
|
885
1306
|
```
|
|
886
1307
|
|
|
1308
|
+
In some cases, the reverse transform is more complex than the forward transform.
|
|
1309
|
+
For that reason, there is a `$flip` property that may be set to `true` on a
|
|
1310
|
+
transform object, to indicate that it is defined from the reverse perspective
|
|
1311
|
+
and should be flipped before running transformations.
|
|
1312
|
+
|
|
1313
|
+
A flipped transformation object will – in forward transformations – get with the
|
|
1314
|
+
properties on the object and set with the paths in the value. The order of paths
|
|
1315
|
+
and operations in a pipeline will also be reversed. Note that this will not
|
|
1316
|
+
affect any operations that behaves differently depending on direction, and they
|
|
1317
|
+
will run as if they were used on a non-flipped transformation object.
|
|
1318
|
+
|
|
1319
|
+
Also note that flipping will not affect the `get` and `set` operations, only
|
|
1320
|
+
path strings set directly as properties on the object or as part of the value
|
|
1321
|
+
(the pipeline). This might change in the future, so you should not use `get` and
|
|
1322
|
+
`set` directly on a flipped transformation object.
|
|
1323
|
+
|
|
1324
|
+
This flipped defintion:
|
|
1325
|
+
|
|
1326
|
+
```javascript
|
|
1327
|
+
const def33flipped = {
|
|
1328
|
+
$flip: true,
|
|
1329
|
+
id: 'key',
|
|
1330
|
+
attributes: {
|
|
1331
|
+
title: ['headline', transform(threeLetters)],
|
|
1332
|
+
age: ['unknown'],
|
|
1333
|
+
},
|
|
1334
|
+
relationships: {
|
|
1335
|
+
author: value('johnf'),
|
|
1336
|
+
},
|
|
1337
|
+
}
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
... is identical to:
|
|
1341
|
+
|
|
1342
|
+
```javascript
|
|
1343
|
+
const def33 = {
|
|
1344
|
+
key: 'id',
|
|
1345
|
+
headline: ['attributes.title', transform(threeLetters)],
|
|
1346
|
+
unknown: ['attributes.age']
|
|
1347
|
+
},
|
|
1348
|
+
'none/1': ['relationships.author': value('johnf')]
|
|
1349
|
+
}
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
The flipped definition is (in this case) easier to read.
|
|
1353
|
+
|
|
1354
|
+
Note also the `'none/1'` property in `def33`, that will stop this property from
|
|
1355
|
+
being set when going forward. This is not necessary on the flipped definition,
|
|
1356
|
+
but also results in a definition that will not work as expected going forward.
|
|
1357
|
+
This is a weakness in how MapTransform treats pipelines right now, and will
|
|
1358
|
+
probably be resolved in the future. For now, make sure to always have a path
|
|
1359
|
+
at the beginning of all pipelines if you plan to reverse transform – and the
|
|
1360
|
+
same goes for flipped transform objects if you want to forward transform.
|
|
1361
|
+
|
|
887
1362
|
### Mapping without fallbacks
|
|
888
1363
|
|
|
889
1364
|
MapTransform will try its best to map the data it gets to the state you want,
|
|
@@ -905,7 +1380,7 @@ import { mapTransform, alt, value } from 'map-transform'
|
|
|
905
1380
|
|
|
906
1381
|
const def24 = {
|
|
907
1382
|
id: 'customerNo',
|
|
908
|
-
name: ['fullname', alt(value('Anonymous'))]
|
|
1383
|
+
name: ['fullname', alt(value('Anonymous'))],
|
|
909
1384
|
}
|
|
910
1385
|
|
|
911
1386
|
const mapper = mapTransform(def24)
|