map-transform 0.3.12 → 0.4.0-alpha.11
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 +641 -98
- package/dist/functions/compare.d.ts +7 -5
- package/dist/functions/compare.js +27 -16
- package/dist/functions/compare.js.map +1 -1
- package/dist/functions/explode.d.ts +7 -0
- package/dist/functions/explode.js +53 -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 +16 -5
- package/dist/functions/index.js +17 -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 +32 -0
- package/dist/functions/joinSplit.js.map +1 -0
- package/dist/functions/logical.d.ts +7 -0
- package/dist/functions/logical.js +24 -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/sort.d.ts +7 -0
- package/dist/functions/sort.js +33 -0
- package/dist/functions/sort.js.map +1 -0
- 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 +3 -3
- 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 +28 -13
- 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 +5 -5
- 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 +10 -5
- 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.d.ts +2 -1
- package/dist/operations/getSet.js +24 -25
- package/dist/operations/getSet.js.map +1 -1
- package/dist/operations/ifelse.d.ts +2 -0
- package/dist/operations/ifelse.js +20 -0
- package/dist/operations/ifelse.js.map +1 -0
- package/dist/operations/iterate.d.ts +2 -0
- package/dist/operations/iterate.js +21 -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 +3 -0
- package/dist/operations/merge.js +43 -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 +84 -7
- package/dist/operations/mutate.js.map +1 -1
- package/dist/operations/pipe.js +19 -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 +3 -2
- package/dist/operations/root.js.map +1 -1
- package/dist/operations/transform.js +4 -4
- 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 +52 -23
- package/dist/utils/array.d.ts +2 -0
- package/dist/utils/array.js +12 -0
- package/dist/utils/array.js.map +1 -0
- package/dist/utils/definitionHelpers.d.ts +7 -8
- package/dist/utils/definitionHelpers.js +80 -47
- package/dist/utils/definitionHelpers.js.map +1 -1
- package/dist/utils/functional.d.ts +3 -0
- package/dist/utils/functional.js +10 -0
- package/dist/utils/functional.js.map +1 -0
- package/dist/utils/is.d.ts +3 -0
- package/dist/utils/is.js +10 -0
- package/dist/utils/is.js.map +1 -0
- package/dist/utils/pathGetter.d.ts +5 -3
- package/dist/utils/pathGetter.js +21 -11
- package/dist/utils/pathGetter.js.map +1 -1
- package/dist/utils/pathSetter.d.ts +5 -4
- package/dist/utils/pathSetter.js +36 -44
- package/dist/utils/pathSetter.js.map +1 -1
- package/dist/utils/stateHelpers.d.ts +8 -36
- package/dist/utils/stateHelpers.js +26 -11
- package/dist/utils/stateHelpers.js.map +1 -1
- package/package.json +16 -56
- 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,54 @@ const def2 = {
|
|
|
180
188
|
// }
|
|
181
189
|
```
|
|
182
190
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
191
|
+
When you transform an array of data with a mapping object, you'll have to set
|
|
192
|
+
`$iterate: true` to have each item in the data array be transformed with the
|
|
193
|
+
mapping object. If you don't the entire array will be passed to the mapping
|
|
194
|
+
object.
|
|
187
195
|
|
|
188
196
|
```javascript
|
|
189
197
|
const def3 = {
|
|
190
|
-
|
|
198
|
+
$iterate: true,
|
|
199
|
+
title: 'heading',
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// -->
|
|
203
|
+
// [
|
|
204
|
+
// { title: 'The first heading' },
|
|
205
|
+
// { title: 'The second heading' }
|
|
206
|
+
// ]
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Note:** Iterating used to be the default behavior, but it has been changed due
|
|
210
|
+
to the contradiction with how the mapping object behaves everywhere else.
|
|
211
|
+
|
|
212
|
+
A key will set whatever is returned by the pipeline (see
|
|
213
|
+
[next section](#values-on-the-transform-object)), whether it is a string, a
|
|
214
|
+
boolean, an array, etc. If you want to ensure that you always get an array, you
|
|
215
|
+
can suffix the key with `[]`. Any value that is not an array will be wrapped in
|
|
216
|
+
one.
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
const def27 = {
|
|
220
|
+
$iterate: false
|
|
221
|
+
'articles[]': {
|
|
191
222
|
title: 'heading'
|
|
192
223
|
}
|
|
193
224
|
}
|
|
194
225
|
|
|
195
|
-
//
|
|
226
|
+
// -->
|
|
196
227
|
// {
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
// ]
|
|
201
|
-
// }
|
|
228
|
+
// articles: [
|
|
229
|
+
// { title: 'Wrapped in an array, even if the data was just an object' },
|
|
230
|
+
// ]
|
|
202
231
|
// }
|
|
203
232
|
```
|
|
204
233
|
|
|
234
|
+
A bonus of using the `[]` suffix, is that when key has another transform object
|
|
235
|
+
as its value, this transform object will be iterated by default (no need to set
|
|
236
|
+
the `$iterate` property). This does not happen to pipelines, paths, or
|
|
237
|
+
operations.
|
|
238
|
+
|
|
205
239
|
#### Values on the transform object
|
|
206
240
|
|
|
207
241
|
The values on the transform objects define how to retrieve and transform data
|
|
@@ -217,7 +251,7 @@ is at this path on the source object.
|
|
|
217
251
|
|
|
218
252
|
```javascript
|
|
219
253
|
const def4 = {
|
|
220
|
-
title: 'data.item.heading'
|
|
254
|
+
title: 'data.item.heading',
|
|
221
255
|
}
|
|
222
256
|
|
|
223
257
|
const source1 = {
|
|
@@ -225,9 +259,9 @@ const source1 = {
|
|
|
225
259
|
item: {
|
|
226
260
|
id: 'item1',
|
|
227
261
|
heading: 'The actual heading',
|
|
228
|
-
intro: 'The actual intro'
|
|
229
|
-
}
|
|
230
|
-
}
|
|
262
|
+
intro: 'The actual intro',
|
|
263
|
+
},
|
|
264
|
+
},
|
|
231
265
|
}
|
|
232
266
|
|
|
233
267
|
// `mapTransform(def4)(source1)` will transform to:
|
|
@@ -251,7 +285,7 @@ an array by indicating the array index in the brackets.
|
|
|
251
285
|
|
|
252
286
|
```javascript
|
|
253
287
|
const def5 = {
|
|
254
|
-
title: 'data.items[0].heading'
|
|
288
|
+
title: 'data.items[0].heading',
|
|
255
289
|
}
|
|
256
290
|
|
|
257
291
|
// def5 will pull the heading from the first item in the `items` array, and will
|
|
@@ -267,6 +301,41 @@ the transform pipeline (which the dot notation path really is, and – come to
|
|
|
267
301
|
think of it – the transform object itself too). This is explained in detail
|
|
268
302
|
below.
|
|
269
303
|
|
|
304
|
+
#### A note on undefined and null
|
|
305
|
+
|
|
306
|
+
MapTransform will treat `undefined` as a value that is not set, and so a
|
|
307
|
+
transform object will not be applied to it. So if an `undefined` value meets
|
|
308
|
+
a transform object, it will produce `undefined`, regardsless of the shape of the
|
|
309
|
+
transform object.
|
|
310
|
+
|
|
311
|
+
This is not the case for `null`, though. MapTransform treats `null` as a value,
|
|
312
|
+
an intended nothing, and will apply a transform object to it, even though it
|
|
313
|
+
will most likely produce nothing but default values. To change this behavior,
|
|
314
|
+
set `mutateNull: false` on the `options` object passed to MapTransform. This
|
|
315
|
+
will essentially make MapTransform treat `null` the same way as `undefined` when
|
|
316
|
+
it comes to the transform object.
|
|
317
|
+
|
|
318
|
+
#### Directional transform objects
|
|
319
|
+
|
|
320
|
+
A transform object is by default applied on forward transformations and in
|
|
321
|
+
reverse. You may alter this by setting the `$direction` prop on a transform
|
|
322
|
+
object, with `fwd`, `rev`, or `both` (the default) as possible values.
|
|
323
|
+
|
|
324
|
+
When running a forward transformation, transform objects marked with
|
|
325
|
+
`$direction: 'rev'` will be skipped. The same goes for `$direction: 'fwd'` in
|
|
326
|
+
reverse.
|
|
327
|
+
|
|
328
|
+
You may specify aliases for `fwd` and `rev` in the `mapTransform` options:
|
|
329
|
+
|
|
330
|
+
```javascript
|
|
331
|
+
const options = { fwdAlias: 'from', revAlias: 'to' }
|
|
332
|
+
const mapper = mapTransform(def, options)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
In this case, `from` and `to` may be used to specify forward and reverse
|
|
336
|
+
direction respectively. `fwd` and `rev` will still work in addition to the
|
|
337
|
+
aliases.
|
|
338
|
+
|
|
270
339
|
### Transform pipeline
|
|
271
340
|
|
|
272
341
|
The idea of the transform pipeline, is that you describe a set of
|
|
@@ -277,7 +346,7 @@ original format again (although with a potential loss of data, if not all
|
|
|
277
346
|
properties are transformed). This is what you do in a
|
|
278
347
|
[reverse mapping](#reverse-mapping).
|
|
279
348
|
|
|
280
|
-
One way to put it is that the pipeline describes the difference between the two
|
|
349
|
+
One way to put it, is that the pipeline describes the difference between the two
|
|
281
350
|
possible states of the data, and allows you to go back and forth between them.
|
|
282
351
|
Or you can just view it as operations applied in the order they are defined – or
|
|
283
352
|
back again.
|
|
@@ -299,24 +368,33 @@ import { transform, filter } from 'map-transform'
|
|
|
299
368
|
const def6 = [
|
|
300
369
|
'data.items[]',
|
|
301
370
|
{
|
|
371
|
+
$iterate: true,
|
|
302
372
|
id: 'articleNo',
|
|
303
373
|
title: ['headline', transform(maxLength(20))],
|
|
304
|
-
sections: 'meta.sections[].id'
|
|
374
|
+
sections: 'meta.sections[].id',
|
|
305
375
|
},
|
|
306
|
-
filter(onlyItemsWithSection)
|
|
376
|
+
filter(onlyItemsWithSection),
|
|
307
377
|
]
|
|
308
378
|
```
|
|
309
379
|
|
|
310
380
|
(Note that in this example, both `maxLength` and `onlyItemsWithSection` are
|
|
311
381
|
custom functions for this case, but their implementations are not provided.)
|
|
312
382
|
|
|
313
|
-
|
|
383
|
+
**A note on arrays:** In a transform pipeline, the default behavior is to treat
|
|
384
|
+
an array as any other data. The array will be passed on to a `transform()`
|
|
385
|
+
operation, the entire array will be set on a path, etc. This also means that a
|
|
386
|
+
mapping object will be applied to the entire array if nothing else is specified.
|
|
387
|
+
In the example above, we have set `$iterate: true` on the mapping object, to
|
|
388
|
+
signal that we want the mapping to be applied to the items of any array. See
|
|
389
|
+
also [the `iterate` operation](#iteratepipeline-operation) for more.
|
|
390
|
+
|
|
391
|
+
#### `transform(transformFn, transformFnRev)` operation
|
|
314
392
|
|
|
315
393
|
The simple beauty of the `transform()` operation, is that it will apply whatever
|
|
316
394
|
function you provide it with to the data at that point in the pipeline. It's
|
|
317
395
|
completely up to you to write the function that does the transformation.
|
|
318
396
|
|
|
319
|
-
You may supply a second function (`
|
|
397
|
+
You may supply a second function (`transformFnRev`), that will be used when
|
|
320
398
|
[reverse mapping](#reverse-mapping). If you only supplies one function, it will
|
|
321
399
|
be used in both directions. You may supply `null` for either of these, to make
|
|
322
400
|
it uni-directional, but it might be clearer to use `fwd()` or `rev()` operations
|
|
@@ -336,16 +414,16 @@ on an object. You would probably just not get the end result you expected.
|
|
|
336
414
|
```javascript
|
|
337
415
|
import { mapTransform, transform } from 'map-transform'
|
|
338
416
|
|
|
339
|
-
const ensureInteger = data => Number.parseInt(data, 10) || 0
|
|
417
|
+
const ensureInteger = (data) => Number.parseInt(data, 10) || 0
|
|
340
418
|
const def7 = {
|
|
341
|
-
count: ['statistics.views', transform(ensureInteger)]
|
|
419
|
+
count: ['statistics.views', transform(ensureInteger)],
|
|
342
420
|
}
|
|
343
421
|
|
|
344
422
|
const data = {
|
|
345
423
|
statistics: {
|
|
346
|
-
view: '18'
|
|
424
|
+
view: '18',
|
|
347
425
|
// ...
|
|
348
|
-
}
|
|
426
|
+
},
|
|
349
427
|
}
|
|
350
428
|
|
|
351
429
|
mapTransform(def7)(data)
|
|
@@ -381,41 +459,50 @@ Example transformation pipeline with a `yearsSince` function:
|
|
|
381
459
|
|
|
382
460
|
```javascript
|
|
383
461
|
const def8 = {
|
|
384
|
-
age: ['birthyear', yearsSince(new Date())]
|
|
462
|
+
age: ['birthyear', yearsSince(new Date())],
|
|
385
463
|
}
|
|
386
464
|
```
|
|
387
465
|
|
|
466
|
+
**Note:** When the `transform` operation is applied to an array, it will not
|
|
467
|
+
iterate the array. Mapping over each item needs to be handled in the transform
|
|
468
|
+
itself, or wrap the `transform` operation in an `iterate` operation.
|
|
469
|
+
|
|
388
470
|
You may also define a transform operation as an object:
|
|
389
471
|
|
|
390
472
|
```javascript
|
|
391
473
|
import { mapTransform } from 'map-transform'
|
|
392
474
|
|
|
393
|
-
const ensureInteger = operands => data => Number.parseInt(data, 10) || 0
|
|
394
|
-
const
|
|
475
|
+
const ensureInteger = (operands) => (data) => Number.parseInt(data, 10) || 0
|
|
476
|
+
const functions = { ensureInteger }
|
|
395
477
|
const def7asObject = {
|
|
396
|
-
count: ['statistics.views', { $transform: 'ensureInteger' }]
|
|
478
|
+
count: ['statistics.views', { $transform: 'ensureInteger' }],
|
|
397
479
|
}
|
|
398
480
|
|
|
399
481
|
const data = {
|
|
400
482
|
statistics: {
|
|
401
|
-
view: '18'
|
|
483
|
+
view: '18',
|
|
402
484
|
// ...
|
|
403
|
-
}
|
|
485
|
+
},
|
|
404
486
|
}
|
|
405
487
|
|
|
406
|
-
mapTransform(def7asObject, {
|
|
488
|
+
mapTransform(def7asObject, { functions })(data)
|
|
407
489
|
// --> {
|
|
408
490
|
// count: 18
|
|
409
491
|
// }
|
|
410
492
|
```
|
|
411
493
|
|
|
412
|
-
Note that the function itself is passed on the `
|
|
494
|
+
Note that the function itself is passed on the `functions` object. When
|
|
413
495
|
you provide the custom function this way, it should be given as a function
|
|
414
496
|
accepting an object with operands / arguments, that returns the actual function
|
|
415
497
|
used in the transform. Any properties given on the operation object, apart from
|
|
416
498
|
`$transform`, will be passed in the `operands` object.
|
|
417
499
|
|
|
418
|
-
|
|
500
|
+
When you define the `transform` operation as an object, you may specify
|
|
501
|
+
`$iterate: true` on the object to apply the transform to every item on an array,
|
|
502
|
+
if an array is encountered. You may also set `$direction: 'fwd'` or
|
|
503
|
+
`$direction: 'rev'` to have it transform in one direction only.
|
|
504
|
+
|
|
505
|
+
#### `filter(conditionFn)` operation
|
|
419
506
|
|
|
420
507
|
Just like the transform operation, the filter operation will apply whatever
|
|
421
508
|
function you give it to the data at that point in the transform pipeline, but
|
|
@@ -458,7 +545,7 @@ Defining a filter operation as an object:
|
|
|
458
545
|
import { mapTransform } from 'map-transform'
|
|
459
546
|
|
|
460
547
|
const onlyActives = (data) => data.active
|
|
461
|
-
const
|
|
548
|
+
const functions = { onlyActives }
|
|
462
549
|
const def9asObject = [
|
|
463
550
|
'members'
|
|
464
551
|
{
|
|
@@ -469,7 +556,120 @@ const def9asObject = [
|
|
|
469
556
|
]
|
|
470
557
|
```
|
|
471
558
|
|
|
472
|
-
|
|
559
|
+
You may also set `$direction: 'fwd'` or `$direction: 'rev'` on the object, to
|
|
560
|
+
have it filter in one direction only.
|
|
561
|
+
|
|
562
|
+
See the `transform()` operation for more on how defining as an object works.
|
|
563
|
+
|
|
564
|
+
#### `ifelse(conditionFn, truePipeline, falsePipeline)` operation
|
|
565
|
+
|
|
566
|
+
The `ifelse()` operation will run the `truePipeline` if the `conditionFn` results
|
|
567
|
+
in `true`, otherwise it will run the `falsePipeline`. See
|
|
568
|
+
[the `filter()` operation](#filterconditionFn-operation) for more on the
|
|
569
|
+
requirements for the `conditionFn`.
|
|
570
|
+
|
|
571
|
+
Both `truePipeline` and `falsePipeline` are optional, in case you only need to
|
|
572
|
+
apply a pipeline in one of the cases.
|
|
573
|
+
|
|
574
|
+
Example:
|
|
575
|
+
|
|
576
|
+
```javascript
|
|
577
|
+
import { mapTransform, ifelse } from 'map-transform'
|
|
578
|
+
|
|
579
|
+
const onlyActives = (data) => data.active
|
|
580
|
+
const def31 = [
|
|
581
|
+
'members'
|
|
582
|
+
{
|
|
583
|
+
name: 'name',
|
|
584
|
+
active: 'hasPayed'
|
|
585
|
+
},
|
|
586
|
+
ifelse(onlyActives, set('active[]'), set('inactive[]'))
|
|
587
|
+
]
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
#### `iterate(pipeline)` operation
|
|
591
|
+
|
|
592
|
+
If you want something to be mapped over the items of an array, the `iterate`
|
|
593
|
+
operation is your friend. When you wrap another operation, a pipeline, or a
|
|
594
|
+
mapping object in an `iterate` operation, it will be applied to each item.
|
|
595
|
+
|
|
596
|
+
When iterating, the state's `context` will iterate as well, to support `alt`
|
|
597
|
+
operations etc. that need to access the corresponding item in the context.
|
|
598
|
+
|
|
599
|
+
In this example, each value in the array returned by `statistics[].views` will
|
|
600
|
+
be transformed with the `ensureInteger` function, even if the function does not
|
|
601
|
+
support arrays:
|
|
602
|
+
|
|
603
|
+
```javascript
|
|
604
|
+
import { mapTransform, iterate } from 'map-transform'
|
|
605
|
+
|
|
606
|
+
const ensureInteger = data => Number.parseInt(data, 10) || 0
|
|
607
|
+
const def26 = [
|
|
608
|
+
counts: ['statistics[].views', iterate(transform(ensureInteger))]
|
|
609
|
+
]
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
The `iterate` operation may also be used to wrap mapping objects in a transform
|
|
613
|
+
pipeline, however, setting `$iterate: true` on the object may be more
|
|
614
|
+
convenient.
|
|
615
|
+
|
|
616
|
+
#### `apply(pipelineId)` operation
|
|
617
|
+
|
|
618
|
+
The apply operation allows pipelines to be reused. Several pipelines may be
|
|
619
|
+
provided on the `pipelines` object on the `options` provided to
|
|
620
|
+
`mapTransform()`, and the keys of the `pipelines` object serves as ids.
|
|
621
|
+
When an id is passed to the apply operation as `pipelinedId`, the pipeline will
|
|
622
|
+
be applied in the place of the apply operation and executed as if it was part of
|
|
623
|
+
the pipeline definition in the first place.
|
|
624
|
+
|
|
625
|
+
```javascript
|
|
626
|
+
import { mapTransform, apply, transform } from 'map-transform'
|
|
627
|
+
|
|
628
|
+
const ensureInteger = (data) => Number.parseInt(data, 10) || 0
|
|
629
|
+
const pipelines = {
|
|
630
|
+
castEntry: {
|
|
631
|
+
title: ['title', transform(String)],
|
|
632
|
+
count: ['count', transform(ensureInteger)],
|
|
633
|
+
},
|
|
634
|
+
}
|
|
635
|
+
const def25 = [
|
|
636
|
+
{
|
|
637
|
+
title: 'heading',
|
|
638
|
+
count: 'statistics.views',
|
|
639
|
+
},
|
|
640
|
+
apply('castEntry'),
|
|
641
|
+
]
|
|
642
|
+
|
|
643
|
+
const data = {
|
|
644
|
+
heading: 'Entry 1',
|
|
645
|
+
statistics: {
|
|
646
|
+
view: '18',
|
|
647
|
+
},
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
mapTransform(def7)(data)
|
|
651
|
+
// --> {
|
|
652
|
+
// title: 'Entry 1',
|
|
653
|
+
// count: 18
|
|
654
|
+
// }
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
You may also define the apply operation as an operation object:
|
|
658
|
+
|
|
659
|
+
```javascript
|
|
660
|
+
const def25 = [
|
|
661
|
+
{
|
|
662
|
+
title: 'heading',
|
|
663
|
+
count: 'statistics.views',
|
|
664
|
+
},
|
|
665
|
+
{ $apply: 'castEntry' },
|
|
666
|
+
]
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
When you define the `apply` operation as an object, you may specify
|
|
670
|
+
`$iterate: true` on the object to apply the pipeline to every item on an array,
|
|
671
|
+
if an array is encountered. You may also set `$direction: 'fwd'` or
|
|
672
|
+
`$direction: 'rev'` to have it apply in one direction only.
|
|
473
673
|
|
|
474
674
|
#### `value(data)` operation
|
|
475
675
|
|
|
@@ -490,12 +690,25 @@ import { value, alt } from 'map-transform'
|
|
|
490
690
|
const def10 = {
|
|
491
691
|
id: 'data.customerNo',
|
|
492
692
|
type: value('customer'),
|
|
493
|
-
name: ['data.name', alt(value('Anonymous'))]
|
|
693
|
+
name: ['data.name', alt(value('Anonymous'))],
|
|
494
694
|
}
|
|
495
695
|
```
|
|
496
696
|
|
|
497
697
|
The operation will not set anything when mapping with `.onlyMappedValues()`.
|
|
498
698
|
|
|
699
|
+
If you want to define the `value` operation as an operation object, use
|
|
700
|
+
`$transform` or `$alt`:
|
|
701
|
+
|
|
702
|
+
```javascript
|
|
703
|
+
const def10asObject = {
|
|
704
|
+
id: 'data.customerNo',
|
|
705
|
+
type: { $transform: 'value', value: 'customer' },
|
|
706
|
+
name: ['data.name', { $alt: 'value', value: 'Anonymous' }],
|
|
707
|
+
}
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
There is also a shortcut for `{ $transform: 'value', value: 'customer' }`: `{ $value: 'customer' }`, which might be useful when typing definitions by hand.
|
|
711
|
+
|
|
499
712
|
#### `fixed(data)` operation
|
|
500
713
|
|
|
501
714
|
The data given to the fixed operation, will be inserted in the pipeline in place
|
|
@@ -507,15 +720,20 @@ will be included when mapping with `.onlyMappedValues()` as well.
|
|
|
507
720
|
|
|
508
721
|
#### `alt(pipeline)` operation
|
|
509
722
|
|
|
510
|
-
The alt operation will apply the
|
|
511
|
-
|
|
512
|
-
|
|
723
|
+
The alt operation will apply the pipeline it is given when the data already in
|
|
724
|
+
the pipeline is `undefined`. This is how you provide default values in
|
|
725
|
+
MapTransform. The pipeline may be as simple as a `value()` operation, a dot
|
|
513
726
|
notation path into the source data, or a full pipeline of several operations.
|
|
514
727
|
|
|
728
|
+
When given an array, the `alt` operation will treat it as a value and do
|
|
729
|
+
nothing, as it is not an `undefined` value. To provide the `alt` operation to
|
|
730
|
+
every item in the array, please use the `iterate` operation.
|
|
731
|
+
|
|
515
732
|
```javascript
|
|
516
|
-
import { alt, transform,
|
|
517
|
-
const
|
|
518
|
-
const
|
|
733
|
+
import { alt, transform, functions } from 'map-transform'
|
|
734
|
+
const { value } = functions
|
|
735
|
+
const currentDate = (data) => new Date()
|
|
736
|
+
const formatDate = (data) => {
|
|
519
737
|
/* implementation not included */
|
|
520
738
|
}
|
|
521
739
|
|
|
@@ -526,30 +744,38 @@ const def11 = {
|
|
|
526
744
|
'data.updateDate',
|
|
527
745
|
alt('data.createDate'),
|
|
528
746
|
alt(transform(currentDate)),
|
|
529
|
-
transform(formatDate)
|
|
530
|
-
]
|
|
747
|
+
transform(formatDate),
|
|
748
|
+
],
|
|
531
749
|
}
|
|
532
750
|
```
|
|
533
751
|
|
|
534
752
|
In the example above, we first try to set the `updatedAt` prop to the data found
|
|
535
753
|
at `data.updateDate` in the source data. If that does not exist (i.e. we get
|
|
536
754
|
`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
|
-
|
|
755
|
+
we still have `undefined`, the second alt will call the customer function
|
|
756
|
+
`currentDate`, that simply returns the current date as a JS object. Finally,
|
|
757
|
+
another transform operation pipes whatever data we get from all of this through
|
|
758
|
+
the `formatDate` function.
|
|
541
759
|
|
|
542
760
|
You may also define an alt operation as an object:
|
|
543
761
|
|
|
544
762
|
```javascript
|
|
545
763
|
const def11asObject = {
|
|
546
764
|
id: 'data.id',
|
|
547
|
-
name: ['data.name', { $
|
|
765
|
+
name: ['data.name', { $alt: 'value', value: 'Anonymous' }],
|
|
766
|
+
updatedAt: [
|
|
767
|
+
'data.updateDate',
|
|
768
|
+
{ $alt: 'get', path: 'data.createDate' },
|
|
769
|
+
{ $alt: 'currentDate' },
|
|
770
|
+
{ $transform: 'formatDate' },
|
|
771
|
+
],
|
|
548
772
|
}
|
|
549
773
|
```
|
|
550
774
|
|
|
551
|
-
|
|
552
|
-
`
|
|
775
|
+
When you define the `alt` operation as an object, you may specify
|
|
776
|
+
`$iterate: true` on the object to provide a default value to every `undefined`
|
|
777
|
+
item on an array, if an array is encountered. You may also set
|
|
778
|
+
`$direction: 'fwd'` or `$direction: 'rev'` to limit it to one direction only.
|
|
553
779
|
|
|
554
780
|
#### `concat(pipeline, pipeline, ...)` operation
|
|
555
781
|
|
|
@@ -561,6 +787,61 @@ This operation will always return an array, even when it is given only one
|
|
|
561
787
|
pipeline that does not return an array. Pipelines that does not result in a
|
|
562
788
|
value (i.e. return `undefined`) will be filtered away.
|
|
563
789
|
|
|
790
|
+
#### `merge(pipeline, pipeline, ...)` operation
|
|
791
|
+
|
|
792
|
+
`merge()` will run all given pipelines and deep merge their results. Conflicts
|
|
793
|
+
are resolved by prioritizing results from the rightmost of the conflicting
|
|
794
|
+
pipelines.
|
|
795
|
+
|
|
796
|
+
#### `modify(pipeline)` operation
|
|
797
|
+
|
|
798
|
+
Use the `modify()` operation when you want the pipeline to modify an object,
|
|
799
|
+
instead of replacing it.
|
|
800
|
+
|
|
801
|
+
Example:
|
|
802
|
+
|
|
803
|
+
```javascript
|
|
804
|
+
import { modify } from 'map-transform'
|
|
805
|
+
|
|
806
|
+
const def34 = modify({
|
|
807
|
+
data: 'data.deeply.placed.items',
|
|
808
|
+
})
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
`def34` will in effect set the values placed at a deep path on the `data`
|
|
812
|
+
prop. Giving this an object like:
|
|
813
|
+
|
|
814
|
+
```javascript
|
|
815
|
+
const response = {
|
|
816
|
+
status: 'ok',
|
|
817
|
+
data: { deeply: { placed: { items: [{ id: 'ent1' }] } } },
|
|
818
|
+
}
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
...will result in:
|
|
822
|
+
|
|
823
|
+
```javascript
|
|
824
|
+
const response = {
|
|
825
|
+
status: 'ok',
|
|
826
|
+
data: [{ id: 'ent1' }],
|
|
827
|
+
}
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
Had we ran this without the `modify()` operation, the returned object would only
|
|
831
|
+
have the `data` prop.
|
|
832
|
+
|
|
833
|
+
This is equivalent to setting the `$modify` property to `true` on the object
|
|
834
|
+
mutation object:
|
|
835
|
+
|
|
836
|
+
```javascript
|
|
837
|
+
const def34b = {
|
|
838
|
+
$modify: true,
|
|
839
|
+
data: 'data.deeply.placed.items',
|
|
840
|
+
}
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
Note that `$modify` may also be set further down in the object structure.
|
|
844
|
+
|
|
564
845
|
#### `fwd(pipeline)` and `rev(pipeline)` operation
|
|
565
846
|
|
|
566
847
|
All operations in MapTransform will apply in both directions, although some of
|
|
@@ -573,11 +854,11 @@ pipeline when we're mapping in reverse.
|
|
|
573
854
|
|
|
574
855
|
```javascript
|
|
575
856
|
import { fwd, rev, transform } from 'map-transform'
|
|
576
|
-
const increment = data => data + 1
|
|
577
|
-
const decrement = data => data - 1
|
|
857
|
+
const increment = (data) => data + 1
|
|
858
|
+
const decrement = (data) => data - 1
|
|
578
859
|
|
|
579
860
|
const def12 = {
|
|
580
|
-
order: ['index', fwd(transform(increment)), rev(transform(decrement))]
|
|
861
|
+
order: ['index', fwd(transform(increment)), rev(transform(decrement))],
|
|
581
862
|
}
|
|
582
863
|
```
|
|
583
864
|
|
|
@@ -630,7 +911,7 @@ This example results in the exact same pipeline as the example above:
|
|
|
630
911
|
|
|
631
912
|
```javascript
|
|
632
913
|
const def14 = {
|
|
633
|
-
'content[]': 'data.items[].content'
|
|
914
|
+
'content[]': 'data.items[].content',
|
|
634
915
|
}
|
|
635
916
|
```
|
|
636
917
|
|
|
@@ -659,13 +940,13 @@ const def15 = [
|
|
|
659
940
|
{
|
|
660
941
|
id: 'id',
|
|
661
942
|
title: 'headline',
|
|
662
|
-
section: root('meta.section')
|
|
663
|
-
}
|
|
943
|
+
section: root('meta.section'),
|
|
944
|
+
},
|
|
664
945
|
]
|
|
665
946
|
|
|
666
947
|
const data = {
|
|
667
948
|
articles: [{ id: '1', headline: 'An article' } /* ... */],
|
|
668
|
-
meta: { section: 'news' }
|
|
949
|
+
meta: { section: 'news' },
|
|
669
950
|
}
|
|
670
951
|
|
|
671
952
|
mapTransform(def15)(data)
|
|
@@ -680,14 +961,14 @@ As you see, every item in the `articles[]` array, will be mapped with the
|
|
|
680
961
|
items without the root operation.
|
|
681
962
|
|
|
682
963
|
There's also a shortcut notation for root, by prefixing a dot notation path with
|
|
683
|
-
|
|
964
|
+
`^`. This only works when the path is used for getting a value, and it will be
|
|
684
965
|
plugged when used as set (i.e., it will return no value). This may be used in
|
|
685
966
|
`get()` and `set()` operations, and in transformation objects.
|
|
686
967
|
|
|
687
968
|
In the following example, `def16` and `def17` is exactly the same:
|
|
688
969
|
|
|
689
970
|
```javascript
|
|
690
|
-
const def16 = get('
|
|
971
|
+
const def16 = get('^meta.section')
|
|
691
972
|
const def17 = divide(root('meta.section'), plug())
|
|
692
973
|
```
|
|
693
974
|
|
|
@@ -727,8 +1008,8 @@ const data = {
|
|
|
727
1008
|
users: [
|
|
728
1009
|
{ id: 'user1', name: 'User 1' },
|
|
729
1010
|
{ id: 'user2', name: 'User 2' },
|
|
730
|
-
{ id: 'user3', name: 'User 3' }
|
|
731
|
-
]
|
|
1011
|
+
{ id: 'user3', name: 'User 3' },
|
|
1012
|
+
],
|
|
732
1013
|
}
|
|
733
1014
|
const mapper = mapTransform(def18)
|
|
734
1015
|
const mappedData = mapper(data)
|
|
@@ -741,31 +1022,237 @@ mapper.rev(mappedData)
|
|
|
741
1022
|
// --> { content: { meta: { authors: ['user1', 'user3'] } } }
|
|
742
1023
|
```
|
|
743
1024
|
|
|
744
|
-
#### `compare(path,
|
|
1025
|
+
#### `compare({ path, operator, match, matchPath, not })` function
|
|
1026
|
+
|
|
1027
|
+
This is a helper function intended for use with the `filter()` operation. You
|
|
1028
|
+
pass a dot notation `path` and a `match` value (string, number, boolean) to
|
|
1029
|
+
`compare()`, and it returns a function that you can pass to `filter()` for
|
|
1030
|
+
filtering away data that does not not have the value set at the provided path.
|
|
745
1031
|
|
|
746
|
-
|
|
747
|
-
notation path
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
1032
|
+
As an alternative to `match`, you may specify a `matchPath`, which is a dot
|
|
1033
|
+
notation path, in which case the match value will be fetched from the provided
|
|
1034
|
+
data.
|
|
1035
|
+
|
|
1036
|
+
The default is to compare the values resulting from `path` and `match` or
|
|
1037
|
+
`matchPath` with equality, but other operations may be set on the `operator`
|
|
1038
|
+
property. Alternatives: `'='`, `'!='`, `'>'`, `'>='`, `'<'`, or `'<='`, or `in`
|
|
1039
|
+
(equality to at least one of the elements in an array).
|
|
1040
|
+
|
|
1041
|
+
If the `path` points to an array, the value is expected to be one of the values
|
|
1042
|
+
in the array.
|
|
1043
|
+
|
|
1044
|
+
Set `not` to `true` to reverse the result of the comparison.
|
|
751
1045
|
|
|
752
1046
|
Here's an example where only data where role is set to 'admin' will be kept:
|
|
753
1047
|
|
|
754
1048
|
```javascript
|
|
755
|
-
import { filter,
|
|
1049
|
+
import { filter, functions } from 'map-transform'
|
|
1050
|
+
const { compare } = functions
|
|
756
1051
|
|
|
757
1052
|
const def19 = [
|
|
758
1053
|
{
|
|
759
1054
|
name: 'name',
|
|
760
|
-
role: 'editor'
|
|
1055
|
+
role: 'editor',
|
|
761
1056
|
},
|
|
762
|
-
filter(compare('role', 'admin'))
|
|
1057
|
+
filter(compare({ path: 'role', operator: '=', match: 'admin' })),
|
|
1058
|
+
]
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
You may also define this with a transform object:
|
|
1062
|
+
|
|
1063
|
+
```javascript
|
|
1064
|
+
const def19o = [
|
|
1065
|
+
{
|
|
1066
|
+
name: 'name',
|
|
1067
|
+
role: 'editor',
|
|
1068
|
+
},
|
|
1069
|
+
{ $filter: 'compare', path: 'role', operator: '=', match: 'admin' },
|
|
1070
|
+
]
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
#### `explode()` function
|
|
1074
|
+
|
|
1075
|
+
Given an object, the `explode` helper function will return an array with one
|
|
1076
|
+
object for each property in the source object, with a `key` property for the
|
|
1077
|
+
property key, and a `value` property for the value.
|
|
1078
|
+
|
|
1079
|
+
When given an array, the `explode` helper will return on object for every item
|
|
1080
|
+
in the array, with a `key` property set to the index number in the source array
|
|
1081
|
+
and a `value` property to the item value.
|
|
1082
|
+
|
|
1083
|
+
When transforming in reverse, `explode` will try to compile an object or an
|
|
1084
|
+
array from an array of key/value objects. If all `key` props are numbers, an
|
|
1085
|
+
array is produced, otherwise an object. Anything that don't match the expected
|
|
1086
|
+
structure will be skipped.
|
|
1087
|
+
|
|
1088
|
+
Example:
|
|
1089
|
+
|
|
1090
|
+
```javascript
|
|
1091
|
+
import { mapTransform, transform, functions } from 'map-transform'
|
|
1092
|
+
const { explode } = functions
|
|
1093
|
+
|
|
1094
|
+
const data = {
|
|
1095
|
+
currencies: { NOK: 1, USD: 0.125, EUR: 0.1 },
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
const def32 = ['currencies', transform(explode())]
|
|
1099
|
+
|
|
1100
|
+
mapTransform(def32)(data)
|
|
1101
|
+
// --> [{ key: 'NOK', value: 1 }, { key: 'USD', value: 0.125 },
|
|
1102
|
+
// { key: 'EUR', value: 0.1 }]
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
Or as a transform object:
|
|
1106
|
+
|
|
1107
|
+
```javascript
|
|
1108
|
+
const def32o = ['currencies', { $transform: 'explode' }]
|
|
1109
|
+
```
|
|
1110
|
+
|
|
1111
|
+
#### `implode()` function
|
|
1112
|
+
|
|
1113
|
+
This is the exact opposite of the `explode` helper, imploding from a service and
|
|
1114
|
+
explode in reverse (to a service). See
|
|
1115
|
+
[the documentation for `explode()`](#explode-function), but remember that the
|
|
1116
|
+
directions will be reversed for `implode()`.
|
|
1117
|
+
|
|
1118
|
+
#### `map(dictionary)` function
|
|
1119
|
+
|
|
1120
|
+
This helper function accepts a dictionary described as an array of tuples, where
|
|
1121
|
+
each tuple holds a from value and a to value. When a data value is given to the
|
|
1122
|
+
`map` helper, it is replaced with a value from the dictionary. For a forward
|
|
1123
|
+
transformation, the first value in the tuple will be matched with the given
|
|
1124
|
+
data value, and the second value will be returned. In reverse transformation,
|
|
1125
|
+
the second value is matched and the first is returned.
|
|
1126
|
+
|
|
1127
|
+
The wildcard value `*` will match any value, and is applied if there is no other
|
|
1128
|
+
match in the dictionary. When the returned value is `*`, the original data value
|
|
1129
|
+
is returned instead.
|
|
1130
|
+
|
|
1131
|
+
The `map` function only supports primitive values, so any object will be mapped to
|
|
1132
|
+
`undefined` or the value given by the wildcard in the dictionary. Arrays will be
|
|
1133
|
+
iterated to map each value in the array.
|
|
1134
|
+
|
|
1135
|
+
Example:
|
|
1136
|
+
|
|
1137
|
+
```
|
|
1138
|
+
import { transform, functions } from 'map-transform'
|
|
1139
|
+
const { map } = functions
|
|
1140
|
+
|
|
1141
|
+
const dictionary = [
|
|
1142
|
+
[200, 'ok'],
|
|
1143
|
+
[404, 'notfound'],
|
|
1144
|
+
['*', 'error']
|
|
763
1145
|
]
|
|
1146
|
+
|
|
1147
|
+
const def28 = {
|
|
1148
|
+
status: ['result', transform(map({ dictionary }))]
|
|
1149
|
+
}
|
|
1150
|
+
```
|
|
1151
|
+
|
|
1152
|
+
When using `map` in an operation object, you may provice a dictionary array
|
|
1153
|
+
or a named dictionary on the `dictionary` property. An example of with a named
|
|
1154
|
+
dictionary:
|
|
1155
|
+
|
|
764
1156
|
```
|
|
1157
|
+
import { mapTransform } from 'map-transform'
|
|
1158
|
+
|
|
1159
|
+
const dictionary = [
|
|
1160
|
+
[200, 'ok'],
|
|
1161
|
+
[404, 'notfound'],
|
|
1162
|
+
['*', 'error']
|
|
1163
|
+
]
|
|
765
1164
|
|
|
766
|
-
|
|
1165
|
+
const def29 = {
|
|
1166
|
+
status: [
|
|
1167
|
+
'result',
|
|
1168
|
+
{ $transform: 'map', dictionary: 'statusCodes' }
|
|
1169
|
+
]
|
|
1170
|
+
}
|
|
767
1171
|
|
|
768
|
-
|
|
1172
|
+
const mapper = mapTransform(def29, { dictionaries: { statusCodes: dictionary } })
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
#### `sort({asc, path})` function
|
|
1176
|
+
|
|
1177
|
+
The `sort` helper function will sort the given array of items in ascending or
|
|
1178
|
+
descending (depending on whether `asc` is `true` or `false`). Ascending is the
|
|
1179
|
+
default. When a `path` is given, the sort is performed on the value at that path
|
|
1180
|
+
on each object in the array. With no `path`, the values in the array are sorted
|
|
1181
|
+
directly.
|
|
1182
|
+
|
|
1183
|
+
Example:
|
|
1184
|
+
|
|
1185
|
+
```javascript
|
|
1186
|
+
import { mapTransform, transform, functions } from 'map-transform'
|
|
1187
|
+
const { sort } = functions
|
|
1188
|
+
|
|
1189
|
+
const data = {
|
|
1190
|
+
items: [{ id: 'ent5' }, { id: 'ent1' }, { id: 'ent3' }],
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
const def35 = {
|
|
1194
|
+
data: ['items', transform(sort({ asc: true, path: 'id' }))],
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
const ret = mapTransform(def35)(data)
|
|
1198
|
+
// --> { caption: 'Bergen by night. By John F.' }
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
The `sort` function is also available through a transform object:
|
|
1202
|
+
|
|
1203
|
+
```javascript
|
|
1204
|
+
const def35o = {
|
|
1205
|
+
data: ['items', { $transform: 'sort', asc: true, path: 'id' }],
|
|
1206
|
+
}
|
|
1207
|
+
```
|
|
1208
|
+
|
|
1209
|
+
#### `template(template)` function
|
|
1210
|
+
|
|
1211
|
+
The `template` helper function takes a [handlebars] template and applies the
|
|
1212
|
+
given data to it. The placeholders in the template is dot notaion paths to
|
|
1213
|
+
fields in the given data. A simple dot (`.`) refers to the data itself, and may
|
|
1214
|
+
be useful when the pipeline data is a string.
|
|
1215
|
+
|
|
1216
|
+
Values will be forced to strings before being inserted in the template.
|
|
1217
|
+
|
|
1218
|
+
Example:
|
|
1219
|
+
|
|
1220
|
+
```javascript
|
|
1221
|
+
import { mapTransform, transform, functions } from 'map-transform'
|
|
1222
|
+
const { template } = functions
|
|
1223
|
+
|
|
1224
|
+
const data = {
|
|
1225
|
+
content: { description: 'Bergen by night', artist: 'John F.' },
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
const def30 = {
|
|
1229
|
+
caption: ['content', transform(template('{{description}}. By {{artist}}'))],
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
const ret = mapTransform(def30)(data)
|
|
1233
|
+
// --> { caption: 'Bergen by night. By John F.' }
|
|
1234
|
+
```
|
|
1235
|
+
|
|
1236
|
+
The `template` function is also available through a transform object:
|
|
1237
|
+
|
|
1238
|
+
```javascript
|
|
1239
|
+
const def30o = {
|
|
1240
|
+
caption: [
|
|
1241
|
+
'content',
|
|
1242
|
+
{ $transform: 'template', template: '{{description}}. By {{artist}}' },
|
|
1243
|
+
],
|
|
1244
|
+
}
|
|
1245
|
+
```
|
|
1246
|
+
|
|
1247
|
+
If you the template is available as part of the data, you may use `templatePath`
|
|
1248
|
+
instead of `template`, and set it to the path of the template in the data. When
|
|
1249
|
+
using the transform object format, this is as simple as it sounds. With the
|
|
1250
|
+
function version, you supply an operand object to `template()` like this:
|
|
1251
|
+
`transform(template({ templatePath: 'options.captionTemplate' }))`.
|
|
1252
|
+
|
|
1253
|
+
#### `validate(path, schema)` function
|
|
1254
|
+
|
|
1255
|
+
This is a helper function for validating the value at the path against a
|
|
769
1256
|
[JSON Schema](http://json-schema.org). We won't go into details of JSON Schema
|
|
770
1257
|
here, and the `validate()` helper simply retrieves the value at the path and
|
|
771
1258
|
validates it according to the provided schema.
|
|
@@ -774,18 +1261,19 @@ Note that if you provide a schema that is always valid, it will be valid even
|
|
|
774
1261
|
when the data has no value at the given path.
|
|
775
1262
|
|
|
776
1263
|
```javascript
|
|
777
|
-
import { filter,
|
|
1264
|
+
import { filter, functions } from 'map-transform'
|
|
1265
|
+
const { validate } = functions
|
|
778
1266
|
|
|
779
1267
|
const def20 = [
|
|
780
1268
|
'items',
|
|
781
1269
|
filter(validate('draft', { const: false })),
|
|
782
1270
|
{
|
|
783
|
-
title: 'heading'
|
|
784
|
-
}
|
|
1271
|
+
title: 'heading',
|
|
1272
|
+
},
|
|
785
1273
|
]
|
|
786
1274
|
```
|
|
787
1275
|
|
|
788
|
-
#### `not(value)`
|
|
1276
|
+
#### `not(value)` function
|
|
789
1277
|
|
|
790
1278
|
`not()` will return `false` when value if truthy and `true` when value is falsy.
|
|
791
1279
|
This is useful for making the `filter()` operation do the opposite of what the
|
|
@@ -794,14 +1282,15 @@ filter function implies.
|
|
|
794
1282
|
Here we filter away all data where role is set to 'admin':
|
|
795
1283
|
|
|
796
1284
|
```javascript
|
|
797
|
-
import { filter,
|
|
1285
|
+
import { filter, functions } from 'map-transform'
|
|
1286
|
+
const { compare } = functions
|
|
798
1287
|
|
|
799
1288
|
const def21 = [
|
|
800
1289
|
{
|
|
801
1290
|
name: 'name',
|
|
802
|
-
role: 'role'
|
|
1291
|
+
role: 'role',
|
|
803
1292
|
},
|
|
804
|
-
filter(not(compare('role', 'admin')))
|
|
1293
|
+
filter(not(compare('role', 'admin'))),
|
|
805
1294
|
]
|
|
806
1295
|
```
|
|
807
1296
|
|
|
@@ -828,14 +1317,14 @@ const def22 = [
|
|
|
828
1317
|
'data.customers[]',
|
|
829
1318
|
{
|
|
830
1319
|
id: 'customerNo',
|
|
831
|
-
name: ['fullname', alt(value('Anonymous'))]
|
|
832
|
-
}
|
|
1320
|
+
name: ['fullname', alt(value('Anonymous'))],
|
|
1321
|
+
},
|
|
833
1322
|
]
|
|
834
1323
|
|
|
835
1324
|
const dataInTargetState = [
|
|
836
1325
|
{ id: 'cust1', name: 'Fred Johnsen' },
|
|
837
|
-
{ id: 'cust2', name: 'Lucy Knight' },
|
|
838
|
-
{ id: 'cust3' }
|
|
1326
|
+
// { id: 'cust2', name: 'Lucy Knight' },
|
|
1327
|
+
{ id: 'cust3' },
|
|
839
1328
|
]
|
|
840
1329
|
|
|
841
1330
|
const dataInSourceState = mapTransform(def22).rev(dataInTargetState)
|
|
@@ -861,15 +1350,15 @@ For example:
|
|
|
861
1350
|
```javascript
|
|
862
1351
|
import { mapTransform, transform } from 'map-transform'
|
|
863
1352
|
|
|
864
|
-
const username = name => name.replace(/\s+/, '.').toLowerCase()
|
|
1353
|
+
const username = (name) => name.replace(/\s+/, '.').toLowerCase()
|
|
865
1354
|
|
|
866
1355
|
const def23 = [
|
|
867
1356
|
'data.customers[]',
|
|
868
1357
|
{
|
|
869
1358
|
id: 'customerNo',
|
|
870
1359
|
name: 'fullname',
|
|
871
|
-
'name/1': ['username', rev(transform(username))]
|
|
872
|
-
}
|
|
1360
|
+
'name/1': ['username', rev(transform(username))],
|
|
1361
|
+
},
|
|
873
1362
|
]
|
|
874
1363
|
|
|
875
1364
|
const dataInTargetState = [{ id: 'cust1', name: 'Fred Johnsen' }]
|
|
@@ -884,6 +1373,60 @@ const dataInSourceState = mapTransform(def23).rev(dataInTargetState)
|
|
|
884
1373
|
// }
|
|
885
1374
|
```
|
|
886
1375
|
|
|
1376
|
+
In some cases, the reverse transform is more complex than the forward transform.
|
|
1377
|
+
For that reason, there is a `$flip` property that may be set to `true` on a
|
|
1378
|
+
transform object, to indicate that it is defined from the reverse perspective
|
|
1379
|
+
and should be flipped before running transformations.
|
|
1380
|
+
|
|
1381
|
+
A flipped transformation object will – in forward transformations – get with the
|
|
1382
|
+
properties on the object and set with the paths in the value. The order of paths
|
|
1383
|
+
and operations in a pipeline will also be reversed. Note that this will not
|
|
1384
|
+
affect any operations that behaves differently depending on direction, and they
|
|
1385
|
+
will run as if they were used on a non-flipped transformation object.
|
|
1386
|
+
|
|
1387
|
+
Also note that flipping will not affect the `get` and `set` operations, only
|
|
1388
|
+
path strings set directly as properties on the object or as part of the value
|
|
1389
|
+
(the pipeline). This might change in the future, so you should not use `get` and
|
|
1390
|
+
`set` directly on a flipped transformation object.
|
|
1391
|
+
|
|
1392
|
+
This flipped defintion:
|
|
1393
|
+
|
|
1394
|
+
```javascript
|
|
1395
|
+
const def33flipped = {
|
|
1396
|
+
$flip: true,
|
|
1397
|
+
id: 'key',
|
|
1398
|
+
attributes: {
|
|
1399
|
+
title: ['headline', transform(threeLetters)],
|
|
1400
|
+
age: ['unknown'],
|
|
1401
|
+
},
|
|
1402
|
+
relationships: {
|
|
1403
|
+
author: value('johnf'),
|
|
1404
|
+
},
|
|
1405
|
+
}
|
|
1406
|
+
```
|
|
1407
|
+
|
|
1408
|
+
... is identical to:
|
|
1409
|
+
|
|
1410
|
+
```javascript
|
|
1411
|
+
const def33 = {
|
|
1412
|
+
key: 'id',
|
|
1413
|
+
headline: ['attributes.title', transform(threeLetters)],
|
|
1414
|
+
unknown: ['attributes.age']
|
|
1415
|
+
},
|
|
1416
|
+
'none/1': ['relationships.author': value('johnf')]
|
|
1417
|
+
}
|
|
1418
|
+
```
|
|
1419
|
+
|
|
1420
|
+
The flipped definition is (in this case) easier to read.
|
|
1421
|
+
|
|
1422
|
+
Note also the `'none/1'` property in `def33`, that will stop this property from
|
|
1423
|
+
being set when going forward. This is not necessary on the flipped definition,
|
|
1424
|
+
but also results in a definition that will not work as expected going forward.
|
|
1425
|
+
This is a weakness in how MapTransform treats pipelines right now, and will
|
|
1426
|
+
probably be resolved in the future. For now, make sure to always have a path
|
|
1427
|
+
at the beginning of all pipelines if you plan to reverse transform – and the
|
|
1428
|
+
same goes for flipped transform objects if you want to forward transform.
|
|
1429
|
+
|
|
887
1430
|
### Mapping without fallbacks
|
|
888
1431
|
|
|
889
1432
|
MapTransform will try its best to map the data it gets to the state you want,
|
|
@@ -905,7 +1448,7 @@ import { mapTransform, alt, value } from 'map-transform'
|
|
|
905
1448
|
|
|
906
1449
|
const def24 = {
|
|
907
1450
|
id: 'customerNo',
|
|
908
|
-
name: ['fullname', alt(value('Anonymous'))]
|
|
1451
|
+
name: ['fullname', alt(value('Anonymous'))],
|
|
909
1452
|
}
|
|
910
1453
|
|
|
911
1454
|
const mapper = mapTransform(def24)
|