iotagent-node-lib 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGES_NEXT_RELEASE +0 -2
  2. package/config.js +5 -5
  3. package/doc/api.md +1540 -298
  4. package/doc/deprecated.md +3 -1
  5. package/doc/development.md +120 -0
  6. package/doc/installationguide.md +3 -6
  7. package/lib/commonConfig.js +7 -10
  8. package/lib/fiware-iotagent-lib.js +0 -10
  9. package/lib/jexlTranformsMap.js +2 -1
  10. package/lib/plugins/bidirectionalData.js +8 -26
  11. package/lib/plugins/expressionPlugin.js +8 -40
  12. package/lib/plugins/jexlParser.js +28 -0
  13. package/lib/services/commands/commandService.js +1 -1
  14. package/lib/services/devices/deviceService.js +2 -1
  15. package/lib/services/ngsi/entities-NGSI-LD.js +13 -57
  16. package/lib/services/ngsi/entities-NGSI-v2.js +145 -108
  17. package/lib/services/northBound/deviceProvisioningServer.js +17 -14
  18. package/lib/templates/createDevice.json +5 -2
  19. package/lib/templates/createDeviceLax.json +7 -5
  20. package/lib/templates/updateDevice.json +5 -2
  21. package/lib/templates/updateDeviceLax.json +3 -5
  22. package/package.json +1 -1
  23. package/test/unit/examples/deviceProvisioningRequests/provisionBidirectionalDevice.json +5 -5
  24. package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +1 -0
  25. package/test/unit/examples/groupProvisioningRequests/bidirectionalGroup.json +4 -4
  26. package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +0 -2
  27. package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +8 -8
  28. package/test/unit/ngsi-ld/plugins/custom-plugin_test.js +152 -0
  29. package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +1 -1
  30. package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +5 -1
  31. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin31.json +0 -8
  32. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +20 -0
  33. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin40.json +42 -0
  34. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin41.json +32 -0
  35. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin25.json +37 -0
  36. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +228 -6
  37. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +1 -2
  38. package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +6 -6
  39. package/test/unit/ngsiv2/plugins/custom-plugin_test.js +151 -0
  40. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +85 -12
  41. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +11 -3
  42. package/doc/advanced-topics.md +0 -626
  43. package/doc/expressionLanguage.md +0 -762
  44. package/lib/plugins/expressionParser.js +0 -205
  45. package/test/unit/expressions/expression-test.js +0 -197
  46. package/test/unit/ngsi-ld/expressions/expressionBasedTransformations-test.js +0 -882
  47. package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +0 -951
  48. package/test/unit/ngsiv2/expressions/expressionCombinedTransformations-test.js +0 -296
@@ -1,762 +0,0 @@
1
- # Measurement Transformation Expression Language
2
-
3
- - [Overview](#overview)
4
- - [Comparison between expression languages](#comparison-between-expression-languages)
5
- - [Configuring expression language used](#configuring-expression-language-used)
6
- - [Measurement transformation](#measurement-transformation)
7
- - [Expression definition](#expression-definition)
8
- - [Expression execution](#expression-execution)
9
- - [Multientity plugin support (`object_id`)](#multientity-plugin-support-object_id)
10
- - [JEXL Based Transformations](#jexl-based-transformations)
11
- - [Examples of JEXL expressions](#examples-of-jexl-expressions)
12
- - [Available functions](#available-functions)
13
- - [Legacy Expression Language Transformations](#legacy-expression-language-transformations)
14
- - [Expressions](#expressions)
15
- - [Types](#types)
16
- - [Values](#values)
17
- - [Variables](#variables)
18
- - [Constants](#constants)
19
- - [Allowed operations](#allowed-operations)
20
- - [Number operations](#number-operations)
21
- - [String operations](#string-operations)
22
- - [Other available operators](#other-available-operators)
23
- - [Examples of expressions](#examples-of-expressions)
24
- - [NGSI v2 support](#ngsi-v2-support)
25
-
26
- ## Overview
27
-
28
- The IoTAgent Library provides an expression language for measurement transformation, that can be used to adapt the
29
- information coming from the South Bound APIs to the information reported to the Context Broker. This is really useful
30
- when you need to adapt measure.
31
-
32
- There are available two differen expression languages `jexl` and `legacy`. The recommended language to use is `jexl`,
33
- which is newer and most powerful.
34
-
35
- ## Comparison between expression languages
36
-
37
- JEXL overpasses the legacy language in several aspects:
38
-
39
- - JEXL supports multiples types: Boolean, String, Number, Object, Array, NULL.
40
- - JEXL also supports the management and creation of JSON structures (including GeoJSON Objects)
41
- - JEXL allows to navigate and [filter](https://github.com/TomFrost/jexl#collections) objects and arrays.
42
- - JEXL supports if..then...else... via [ternary operator](https://github.com/TomFrost/jexl#ternary-operator).
43
- - JEXL additionally supports the following operations: Divide and floor `//`, Modulus `%`, Logical AND `&&` and
44
- Logical OR `||`. Negation operator is `!`
45
- - JEXL supports [comparisons](https://github.com/TomFrost/jexl#comparisons).
46
- - JEXL supports defining custom transformations (JEXL functions are not currently supported in Perseo).
47
-
48
- For more details, check JEXL language details [here](https://github.com/TomFrost/jexl#all-the-details).
49
-
50
- ## Configuring expression language used
51
-
52
- By default, in order to maintain backward compatibility, `legacy` language is applied. There are different levels to
53
- configure the expression language used:
54
-
55
- - At global level.
56
- - At service group level.
57
- - At device level.
58
-
59
- **Setting the expression language at global level**
60
-
61
- It is possible to set the default language at global level by setting the
62
- [global configuration parameter](installationguide.md#global-configuration) `defaultExpressionLanguage` or the
63
- [environment variable](installationguide.md#configuration-using-environment-variables)
64
- `IOTA_DEFAULT_EXPRESSION_LANGUAGE`. This option configures the default expression language used to compute expressions,
65
- possible values are: `legacy` or `jexl`. When not set or wrongly set, `legacy` is used as default value.
66
-
67
- **Setting the expression language at service group level**
68
-
69
- It is possible to define the expression language at service group by adding the `expressionLanguage` parameter. It is
70
- optional and the possible values are: `legacy` or `jexl`. When not set or wrongly set, `legacy` is used as default. You
71
- can check the following example:
72
-
73
- ```json
74
- {
75
- "services": [
76
- {
77
- "apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
78
- "cbHost": "http://orion:1026",
79
- "entity_type": "Thing",
80
- "resource": "/iot/d"
81
- "expressionLanguage": "jexl",
82
- "attributes": [...]
83
- }
84
- ]
85
- }
86
- ```
87
-
88
- **Setting the expression language at device level**
89
-
90
- Expression language can be configured at device level by adding the `expressionLanguage` parameter to the device
91
- provisioning payload. It is optional and the possible values are: `legacy` or `jexl`. When not set or wrongly set,
92
- `legacy` is used as default. The following example shows how to provision a device using `jexl` language.
93
-
94
- ```json
95
- {
96
- "devices":[
97
- {
98
- "device_id":"45",
99
- "protocol":"GENERIC_PROTO",
100
- "entity_name":"WasteContainer:WC45",
101
- "entity_type":"WasteContainer",
102
- "expressionLanguage": "jexl",
103
- "attributes":[...]
104
- }
105
- ]
106
- }
107
- ```
108
-
109
- ## Measurement transformation
110
-
111
- ### Expression definition
112
-
113
- Expressions can be defined for Active attributes, either in the Device provisioning or in the Configuration
114
- provisioning. The following example shows a device provisioning payload with defined expressions (using the
115
- [JEXL Expression language](#jexl-based-transformations)):
116
-
117
- ```json
118
- {
119
- "devices": [
120
- {
121
- "device_id": "45",
122
- "protocol": "GENERIC_PROTO",
123
- "entity_name": "WasteContainer:WC45",
124
- "entity_type": "WasteContainer",
125
- "expressionLanguage": "jexl",
126
- "attributes": [
127
- {
128
- "name": "location",
129
- "type": "geo:json",
130
- "expression": "{coordinates: [longitude,latitude], type: 'Point'}"
131
- },
132
- {
133
- "name": "fillingLevel",
134
- "type": "Number",
135
- "expression": "level / 100"
136
- },
137
- {
138
- "name": "level",
139
- "type": "Number"
140
- },
141
- {
142
- "name": "latitude",
143
- "type": "Number"
144
- },
145
- {
146
- "name": "longitude",
147
- "type": "Number"
148
- }
149
- ]
150
- }
151
- ]
152
- }
153
- ```
154
-
155
- [Interactive expression `{coordinates: [longitude,latitude], type: 'Point'}`][1]
156
-
157
- [Interactive expression `level / 100`][2]
158
-
159
- The value of the `expression` attribute is a string that can contain any number of expression patterns. In order to
160
- complete expression to be evaluated, all the expression patterns must be evaluable (there must be a value in the
161
- measurement for all the variables of all the expression patterns).
162
-
163
- Note that you need to include in the provision operation all the attributes required as inputs for the expressions. In
164
- this example, they are `level`, `latitude` and `longitude`. Otherwise the device sending the measures will get
165
- `{"name":"ATTRIBUTE_NOT_FOUND","message":"Some of the attributes does not exist"}` when it sends some of these and the
166
- expression will not be calculated.
167
-
168
- The exact same syntax works for Configuration and Device provisioning.
169
-
170
- ### Expression execution
171
-
172
- Whenever a new measurement arrives to the IoT Agent for a device with declared expressions, all of the expressions for
173
- the device will be checked for execution: for all the defined active attributes containing expressions, the IoT Agent
174
- will check which ones contain expressions whose variables are present in the received measurement. For all of those
175
- whose variables are covered, their expressions will be executed with the received values, and their values updated in
176
- the Context Broker.
177
-
178
- E.g.: if a device with the following provisioning information is provisioned in the IoT Agent:
179
-
180
- ```json
181
- {
182
- "name":"location",
183
- "type":"geo:point",
184
- "expression": "longitude+', '+latitude"
185
- },
186
- {
187
- "name":"fillingLevel",
188
- "type":"Number",
189
- "expression": "level / 100",
190
- },
191
- ```
192
-
193
- [Interactive expression `longitude+', '+latitude`][3]
194
-
195
- [Interactive expression `level / 100`][4]
196
-
197
- and a measurement with the following values arrive to the IoT Agent:
198
-
199
- ```text
200
- latitude: 1.9
201
- level: 85.3
202
- ```
203
-
204
- The only expression rule that will be executed will be that of the `fillingLevel` attribute. It will produce the value
205
- `0.853` that will be sent to the Context Broker.
206
-
207
- Note that expressions are only applied if the attribute name (as received by the IoT Agent in the southbound interface)
208
- matches the expression variable. Otherwise, the southbound value is used directly. Let's illustrate with the following
209
- example:
210
-
211
- ```json
212
- "consumption": {
213
- "type": "String",
214
- "value": "spaces | trim"
215
- }
216
- ```
217
-
218
- - Case 1: the following measure is received at the southbound interface:
219
-
220
- ```text
221
- consumption: "0.44"
222
- ```
223
-
224
- As `spaces` attribute is not included, then the expression is not applied and the `consumption` measure value is
225
- directly used, so the following is sent to CB:
226
-
227
- ```json
228
- "consumption": {
229
- "type": "String",
230
- "value": "0.44"
231
- }
232
- ```
233
-
234
- - Case 2: the following measure is received at the southbound interface:
235
-
236
- ```text
237
- consumption: "0.44"
238
- spaces: " foobar "
239
- ```
240
-
241
- As `spaces` attribute is included, then the expression is evaluated, so overriding the 0.44 value and sending the
242
- following to CB:
243
-
244
- ```json
245
- "consumption": {
246
- "type": "String",
247
- "value": "foobar"
248
- }
249
- ```
250
-
251
- [Interactive expression `spaces | trim`][5]
252
-
253
- ### Multientity plugin support (`object_id`)
254
-
255
- To allow support for expressions in combination with multi entity plugin, where the same attribute is generated for
256
- different entities out of different incoming attribute values (i.e. `object_id`), we introduced support for `object_id`
257
- in the expression context.
258
-
259
- For example, the following device:
260
-
261
- ```json
262
- "WeatherStation": {
263
- "commands": [],
264
- "type": "WeatherStation",
265
- "lazy": [],
266
- "active": [
267
- {
268
- "object_id": "v1",
269
- "name": "vol",
270
- "expression" : "v1*100",
271
- "type": "Number",
272
- "entity_name": "WeatherStation1"
273
- },
274
- {
275
- "object_id": "v2",
276
- "name": "vol",
277
- "expression" : "v2*100",
278
- "type": "Number",
279
- "entity_name": "WeatherStation2"
280
- },
281
- {
282
- "object_id": "v",
283
- "name": "vol",
284
- "expression" : "v*100",
285
- "type": "Number"
286
- }
287
- ]
288
- }
289
- ```
290
-
291
- When receiving the attributes `v`, `v1` and `v2` in a payload from a message received in the southbound:
292
-
293
- ```json
294
- ({
295
- "name": "v",
296
- "type": "Number",
297
- "value": 0
298
- },
299
- {
300
- "name": "v1",
301
- "type": "Number",
302
- "value": 1
303
- },
304
- {
305
- "name": "v2",
306
- "type": "Number",
307
- "value": 2
308
- })
309
- ```
310
-
311
- Will now generate the following NGSI v2 payload:
312
-
313
- ```json
314
- {
315
- "actionType": "append",
316
- "entities": [
317
- {
318
- "id": "ws9",
319
- "type": "WeatherStation",
320
- "vol": {
321
- "type": "Number",
322
- "value": 0
323
- }
324
- },
325
- {
326
- "vol": {
327
- "type": "Number",
328
- "value": 100
329
- },
330
- "type": "WeatherStation",
331
- "id": "WeatherStation1"
332
- },
333
- {
334
- "vol": {
335
- "type": "Number",
336
- "value": 200
337
- },
338
- "type": "WeatherStation",
339
- "id": "WeatherStation2"
340
- }
341
- ]
342
- }
343
- ```
344
-
345
- ## Available keys for all Expressions
346
-
347
- Apart from measurement transformations (including multientity cases), expressions can be used in the following cases:
348
-
349
- - Default EntityName defined at group level and used for provision and autoprovision of devices
350
- - Commands (push and pull)
351
- - Endpoint of push http commands
352
-
353
- In all of them the following device data is available to all expressions
354
-
355
- - `id`: device ID
356
- - `entity_name`: NGSI entity Name (principal)
357
- - `type`: NGSI entity type (principal)
358
- - `service`: device service
359
- - `subservice`: device subservice
360
-
361
- ## JEXL Based Transformations
362
-
363
- The recommended expression language for the IoTAgent Library is [JEXL](https://github.com/TomFrost/jexl). To use JEXL,
364
- you will need to either configure it as default language using the `defaultExpressionLanguage` field to `jexl` (see
365
- [configuration documentation](installationguide.md)) or configuring the usage of JEXL as expression language for a given
366
- group or device (as shown above).
367
-
368
- ```json
369
- {
370
- "devices": [
371
- {
372
- "device_id": "45",
373
- "protocol": "GENERIC_PROTO",
374
- "entity_name": "WasteContainer:WC45",
375
- "entity_type": "WasteContainer",
376
- "expressionLanguage": "jexl",
377
- "attributes": [
378
- {
379
- "name": "location",
380
- "type": "geo:json",
381
- "expression": "{coordinates: [longitude,latitude], type: 'Point'}"
382
- },
383
- {
384
- "name": "fillingLevel",
385
- "type": "Number",
386
- "expression": "level / 100"
387
- },
388
- {
389
- "name": "level",
390
- "type": "Number"
391
- },
392
- {
393
- "name": "latitude",
394
- "type": "Number"
395
- },
396
- {
397
- "name": "longitude",
398
- "type": "Number"
399
- }
400
- ]
401
- }
402
- ]
403
- }
404
- ```
405
-
406
- In the following we provide examples of using JEXL to apply transformations.
407
-
408
- ### Examples of JEXL expressions
409
-
410
- The following table shows expressions and their expected outcomes taking into account the following measures at
411
- southbound interface:
412
-
413
- - `value` with value 6 (number)
414
- - `ts` with value 1637245214901 (unix timestamp)
415
- - `name` with value `"DevId629"` (string)
416
- - `object` with value `{name: "John", surname: "Doe"}` (JSON object)
417
- - `array` with value `[1, 3]` (JSON Array)
418
-
419
- | Expression | Expected outcome | Format | Playground |
420
- | :-------------------------------------------- | :---------------------------------------- | ------------------- | ------------- |
421
- | `5 * value` | `30` | Integer | [Example][6] |
422
- | `(6 + value) * 3` | `36` | Integer | [Example][7] |
423
- | `value / 12 + 1` | `1.5` | Float | [Example][8] |
424
- | `(5 + 2) * (value + 7)` | `91` | Integer | [Example][9] |
425
- | `value * 5.2` | `31.2` | Float | [Example][10] |
426
- | `"Pruebas " + "De Strings"` | `"Pruebas De Strings"` | String | [Example][11] |
427
- | `name + "value is " +value` | `"DevId629 value is 6"` | String | [Example][12] |
428
- | `{coordinates: [value,value], type: 'Point'}` | `{"coordinates": [6,6], "type": "Point"}` | GeoJSON `Object` | [Example][13] |
429
- | <code>ts&vert;toisodate</code> | `2021-11-18T14:20:14.901Z` | ISO 8601 `DateTime` | [Example][14] |
430
-
431
- Support for `trim`, `length`, `substr` and `indexOf` transformations was added.
432
-
433
- | Expression | Expected outcome | Playground |
434
- | :-------------------------------------------------------- | :--------------- | ------------- |
435
- | <code>" a "&vert; trim</code> | `a` | [Example][15] |
436
- | <code>name&vert;length</code> | `8` | [Example][16] |
437
- | <code>name&vert;indexOf("e")</code> | `1` | [Example][17] |
438
- | <code>name&vert;substr(0,name&vert;indexOf("e")+1)</code> | `"De"` | [Example][18] |
439
-
440
- The following are some examples of **JEXL** expressions not supported by the **legacy** expression language:
441
-
442
- | Expression | Expected outcome | Format | Playground |
443
- | :-------------------------------------------------- | :---------------------------------- | ----------- | ------------- |
444
- | `value == 6? true : false` | `true` | Boolean | [Example][19] |
445
- | <code>value == 6 && name&vert;indexOf("e")>0</code> | `true` | Boolean | [Example][20] |
446
- | `array[1]+1` | `3` | Number | [Example][21] |
447
- | `object.name` | `"John"` | String | [Example][22] |
448
- | `{type:"Point",coordinates: [value,value]}` | `{type:"Point",coordinates: [6,6]}` | JSON Object | [Example][23] |
449
-
450
- ### Available functions
451
-
452
- There are several predefined JEXL transformations available to be used at any JEXL expression. The definition of those
453
- transformations and their JavaScript implementation can be found at jexlTransformsMap.js.
454
-
455
- The library module also exports a method `iotAgentLib.dataPlugins.expressionTransformation.setJEXLTransforms(Map)` to be
456
- used by specific IoT Agent implementations in order to incorporate extra transformations to this set. It is important to
457
- remark that the lib `jexlTransformsMap` cannot be overwritten by the API additions. The idea behind this is to be able
458
- to incorporate new transformations from the IoT Agent configuration file in a fast and tactical way.
459
-
460
- Current common transformation set:
461
-
462
- | JEXL Transformation | Equivalent JavaScript Function |
463
- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
464
- | jsonparse: (str) | `JSON.parse(str);` |
465
- | jsonstringify: (obj) | `JSON.stringify(obj);` |
466
- | indexOf: (val, char) | `String(val).indexOf(char);` |
467
- | length: (val) | `String(val).length;` |
468
- | trim: (val) | `String(val).trim();` |
469
- | substr: (val, int1, int2) | `String(val).substr(int1, int2);` |
470
- | addreduce: (arr) | <code>arr.reduce((i, v) &vert; i + v));</code> |
471
- | lengtharray: (arr) | `arr.length;` |
472
- | typeof: (val) | `typeof val;` |
473
- | isarray: (arr) | `Array.isArray(arr);` |
474
- | isnan: (val) | `isNaN(val);` |
475
- | parseint: (val) | `parseInt(val);` |
476
- | parsefloat: (val) | `parseFloat(val);` |
477
- | toisodate: (val) | `new Date(val).toISOString();` |
478
- | timeoffset:(isostr) | `new Date(isostr).getTimezoneOffset();` |
479
- | tostring: (val) | `val.toString();` |
480
- | urlencode: (val) | `encodeURI(val);` |
481
- | urldecode: (val) | `decodeURI(val);` |
482
- | replacestr: (str, from, to) | `str.replace(from, to);` |
483
- | replaceregexp: (str, reg, to) | `str.replace(new RegExp(reg), to);` |
484
- | replaceallstr: (str, from, to) | `str.replaceAll(from, to);` |
485
- | replaceallregexp: (str, reg, to) | `str.replaceAll(new RegExp(reg,"g"), to);` |
486
- | split: (str, ch) | `str.split(ch);` |
487
- | joinarrtostr: (arr, ch) | `arr.join(ch);` |
488
- | concatarr: (arr, arr2) | `arr.concat(arr2);` |
489
- | mapper: (val, values, choices) | <code>choices[values.findIndex((target) &vert; target == val)]);</code> |
490
- | thmapper: (val, values, choices) | <code>choices[values.reduce((acc,curr,i,arr) &vert; (acc==0)&vert;&vert;acc?acc:val<=curr?acc=i:acc=null,null)];</code> |
491
- | bitwisemask: (i,mask,op,shf) | <code>(op==="&"?parseInt(i)&mask: op==="&vert;"?parseInt(i)&vert;mask: op==="^"?parseInt(i)^mask:i)>>shf;</code> |
492
- | slice: (arr, init, end) | `arr.slice(init,end);` |
493
- | addset: (arr, x) | <code>{ return Array.from((new Set(arr)).add(x)) }</code> |
494
- | removeset: (arr, x) | <code>{ let s = new Set(arr); s.delete(x); return Array.from(s) }</code> |
495
- | touppercase: (val) | `String(val).toUpperCase()` |
496
- | tolowercase: (val) | `String(val).toLowerCase()` |
497
- | round: (val) | `Math.round(val)` |
498
- | floor: (val) | `Math.floor(val)` |
499
- | ceil: (val) | `Math.ceil(val)` |
500
- | tofixed: (val, decimals) | `Number.parseFloat(val).toFixed(decimals)` |
501
- | gettime: (d) | `new Date(d).getTime()` |
502
- | toisostring: (d) | `new Date(d).toISOString()` |
503
- | localestring: (d, timezone, options) | `new Date(d).toLocaleString(timezone, options)` |
504
- | now: () | `Date.now()` |
505
-
506
- You have available this [JEXL interactive playground][99] with all the transformations already loaded, in which you can
507
- test all the functions described above.
508
-
509
- ## Legacy Expression Language Transformations
510
-
511
- As described in previous sections, this is the default language just for backward compatibility reasons, but **we
512
- strongly encourage you not to use this language** for defining expression in order to transform measures, since it is
513
- not capable to handle JSON native types, it is not extensible and many other reasons as described on the section
514
- [Comparison between expression languages](#comparison-between-expression-languages). The following example shows a
515
- device provisioning payload with defined expressions using the Legacy Expression Language:
516
-
517
- ```json
518
- {
519
- "devices": [
520
- {
521
- "device_id": "45",
522
- "protocol": "GENERIC_PROTO",
523
- "entity_name": "WasteContainer:WC45",
524
- "entity_type": "WasteContainer",
525
- "attributes": [
526
- {
527
- "name": "location",
528
- "type": "geo:point",
529
- "expression": "${@latitude}, ${@longitude}"
530
- },
531
- {
532
- "name": "fillingLevel",
533
- "type": "Number",
534
- "expression": "${@level / 100}"
535
- },
536
- {
537
- "name": "level",
538
- "type": "Number"
539
- },
540
- {
541
- "name": "latitude",
542
- "type": "Number"
543
- },
544
- {
545
- "name": "longitude",
546
- "type": "Number"
547
- }
548
- ]
549
- }
550
- ]
551
- }
552
- ```
553
-
554
- ### Expressions
555
-
556
- Each expression pattern is marked with the following secuence: `${<expression>}` where `<expression>` is a construction
557
- with a valid syntax.
558
-
559
- ### Types
560
-
561
- The way the `parse()` function works (at `expressionParser.js`) is as follows:
562
-
563
- - Expressions can have two return types: `String` or `Number`. This return type must be configured for each attribute
564
- that is going to be converted. Default value type is `String`.
565
- - Whenever an expression is executed without error, its result will be cast to the configured type. If the conversion
566
- fails (e.g.: if the expression is null or a String and is cast to Number), the measurement update will fail, and an
567
- error will be reported to the device.
568
-
569
- However, the usage that the Expression Translation plugin does of that function is using always `String` type. That
570
- means that at the end, the result of the expression will be always cast to `String`. However, in NGSI v2 that `String`
571
- result could be re-cast to the right type (i.e. the one defined for the attribute in the provision operation). Have a
572
- look at the [NGSI v2 support](#ngsiv2) for more information on this.
573
-
574
- ### Values
575
-
576
- #### Variables
577
-
578
- All the information reported in the measurement received by the IoT Agent is available for the expression to use. For
579
- every attribute coming from the South Bound, a variable with the syntax `@<object_id>` will be created for its use in
580
- the expression language.
581
-
582
- Attribute expressions can contain values taken from the value of other attributes. Those values have, by default, the
583
- String type. For most arithmetic operations (`*`, `/`, etc...) if a variable is involved, its value will be cast to
584
- Number, regardless of the original type. For, example, if a variable `@humidity` has the value `'50'` (a String value),
585
- the following expression:
586
-
587
- ```
588
- ${@humidity * 10}
589
- ```
590
-
591
- will give `500` as the result (i.e.: the value `'50'` is cast to number, to get `50`, that is then multiplied by 10). If
592
- this cast fails (because the value of the variable is not a number, e.g.: `'Fifty'`), the overall result will be `NaN`.
593
-
594
- #### Constants
595
-
596
- The expression language allows for two kinds of constants:
597
-
598
- - Numbers (integer or float)
599
- - Strings (marked with double quotes)
600
-
601
- Current allowed characters are:
602
-
603
- - All the alphanumerical characters
604
- - Whitespaces
605
-
606
- ### Allowed operations
607
-
608
- The following operations are currently available, divided by attribute type
609
-
610
- #### Number operations
611
-
612
- - multiplication ('\*')
613
- - division ('/')
614
- - addition ('+')
615
- - subtraction ('-' binary)
616
- - negation ('-' unary)
617
- - power ('^')
618
-
619
- #### String operations
620
-
621
- - concatenation ('#'): returns the concatenation of the two values separated by `#`.
622
- - substring location (`indexOf(<variable>, <substring>)`): returns the index where the first occurrence of the
623
- substring `<substring>` can be found in the string value of `<variable>`.
624
- - substring (`substr(<variable>, <start> <end>)`): returns a substring of the string variable passed as a parameter,
625
- starting at posisiton `start` and ending at position `<end>`.
626
- - whitespace removal (`trim(<string>)`): removes all the spaces surrounding the string passed as a parameter.
627
-
628
- #### Other available operators
629
-
630
- Parenthesis can be used to define precedence in the operations. Whitespaces between tokens are generally ignored.
631
-
632
- ### Examples of expressions
633
-
634
- The following table shows expressions and their expected outcomes for a measure with two attributes: `@value` with value
635
- `6` and `@name` with value `DevId629`.
636
-
637
- | Expression | Expected outcome | Format |
638
- | :-------------------------- | :-------------------- | ------ |
639
- | `5 \* @value` | `30` | Number |
640
- | `(6 + @value) \* 3` | `36` | Number |
641
- | `@value / 12 + 1` | `1.5` | Number |
642
- | `(5 + 2) \* (@value + 7)` | `91` | Number |
643
- | `@value \* 5.2` | `31.2` | Number |
644
- | `"Pruebas " + "De Strings"` | `Pruebas De Strings` | String |
645
- | `@name value is @value` | `DevId629 value is 6` | String |
646
-
647
- ### NGSI v2 support
648
-
649
- As it is explained in previous sections, expressions can have two return types: `String` or `Number`, being the former
650
- one the default. Whenever an expression is executed without error, its result will be cast to the configured type.
651
-
652
- NGSI v2 and NGSI-LD fully supports all the types described in the JSON specification (`string`, `number`, `boolean`,
653
- `object`, `array` and `null`). Therefore, the result of an expression must be cast to the appropriate type (the type
654
- used to define the attribute) in order to avoid inconsistencies between the type field for an attribute and the type of
655
- the value that is being sent.
656
-
657
- The expression parser for legacy does not support JSON Arrays and JSON document. For the rest of types the workflow will
658
- be the following:
659
-
660
- 1. Variables will be cast to String no matter the expression type (see [comments above](#types) regarding this)
661
- 2. The expression will be applied
662
- 3. The output type will be cast again to the original attribute type.
663
-
664
- - If attribute type is `Number` and the value is an `Integer`, then the value is casted to integer (JSON number)
665
- - If attribute type is `Number` and the value is a `Float`, then the value is casted to float (JSON number)
666
- - If attribute type is `Boolean` then the value is cast to boolean (JSON boolean). In order to do this conversion,
667
- only `true` or `1` are cast to true.
668
- - If attribute type is `None` then the value is cast to `null` (JSON null)
669
-
670
- > **Note**. All the operations and castings described above are not performed using JELX, because the user has full
671
- > control on the final value of the attributes, so there is no need of adding a layer of autocast that would interfere
672
- > and makes things more complicated.
673
-
674
- E.g.: if a device with the following provisioning information is provisioned in the IoT Agent:
675
-
676
- ```json
677
- {
678
- "name": "status",
679
- "type": "Boolean",
680
- "expression": "${@status * 20}"
681
- }
682
- ```
683
-
684
- and a measurement with the following values arrive to the IoT Agent:
685
-
686
- ```
687
- status: true
688
- ```
689
-
690
- 1. The expression `*` is a multiplication, so the expression type makes `status` to be casted to Number. The cast of
691
- `true` to number is 1.
692
- 2. Expression is evaluated, resulting in 20
693
- 3. 20 is cast to `20` since Expression Plugin always use String as Expression type.
694
- 4. The attribute type is `Boolean` so the result is casted to Boolean before sending it to CB. The cast of `20` to
695
- boolean is false (only `true` or `1` are cast to true).
696
-
697
- More examples of this workflow are presented below for the different types of attributes supported in NGSI v2 and the
698
- two possible types of expressions: Integer (arithmetic operations) or Strings.
699
-
700
- - `pressure` with value 52 (integer)
701
- - `consumption` with value 0.44 (float)
702
- - `active` with value `null` (None type)
703
-
704
- | Expression | Expected outcome | Format |
705
- | :---------------------- | :--------------- | ------- |
706
- | `${@pressure * 20}` | `1040` | Integer |
707
- | `${trim(@pressure)}` | `52` | Integer |
708
- | `${@consumption * 20}` | `8.8` | Float |
709
- | `${trim(@consumption)}` | `0.44` | Float |
710
- | `${@pressure * 20}` | `1040` | Integer |
711
- | `${@active * 20}` | `null` | None |
712
- | `${trim(@active)` | `null` | None |
713
- | `${trim(@consumption)}` | `0.44` | Float |
714
-
715
- [1]:
716
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22longitude%22%3A%205%2C%0A%20%20%22latitude%22%3A%2037%2C%0A%20%20%22level%22%3A223%0A%7D&input=%7Bcoordinates%3A%20%5Blongitude%2Clatitude%5D%2C%20type%3A%20'Point'%7D&transforms=%7B%0A%7D
717
- [2]:
718
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22longitude%22%3A%205%2C%0A%20%20%22latitude%22%3A%2037%2C%0A%20%20%22level%22%3A223%0A%7D&input=level%2F100&transforms=%7B%0A%7D
719
- [3]:
720
- https://czosel.github.io/jexl-playground/#/?context=%7B%20%0A%20%20%22latitude%22%3A%201.9%2C%0A%20%20%22level%22%3A85.3%0A%7D&input=longitude%2B%22%2C%20%22%2Blatitude&transforms=%7B%0A%7D
721
- [4]:
722
- https://czosel.github.io/jexl-playground/#/?context=%7B%20%0A%20%20%22latitude%22%3A%201.9%2C%0A%20%20%22level%22%3A85.3%0A%7D&input=level%2F100&transforms=%7B%0A%7D
723
- [5]:
724
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22spaces%22%20%3A%20%22%20%20foobar%20%20%22%0A%7D&input=spaces%20%7C%20trim&transforms=%7B%0A%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%0A%7D
725
- [6]:
726
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=5%20*%20value&transforms=%7B%0A%7D
727
- [7]:
728
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=(6%20%2B%20value)%20*%203&transforms=%7B%0A%7D
729
- [8]:
730
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20%2F%2012%20%2B%201&transforms=%7B%0A%7D
731
- [9]:
732
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=(5%20%2B%202)%20*%20(value%20%2B%207)&transforms=%7B%0A%7D
733
- [10]:
734
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20*%205.2&transforms=%7B%0A%7D
735
- [11]:
736
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%22Pruebas%20%22%20%2B%20%22De%20Strings%22&transforms=%7B%0A%7D
737
- [12]:
738
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%20%2B%20%22value%20is%20%22%20%2Bvalue&transforms=%7B%0A%7D
739
- [13]:
740
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%7Bcoordinates%3A%20%5Bvalue%2Cvalue%5D%2C%20type%3A%20'Point'%7D&transforms=%7B%0A%7D
741
- [14]:
742
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=ts%7Ctoisodate&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
743
- [15]:
744
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%22%20a%20%22%7Ctrim&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
745
- [16]:
746
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7Clength&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
747
- [17]:
748
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7CindexOf(%22e%22)&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
749
- [18]:
750
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7Csubstr(0%2Cname%7CindexOf(%22e%22)%2B1)&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
751
- [19]:
752
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20%3D%3D%206%3F%20true%20%3A%20false&transforms=%7B%7D
753
- [20]:
754
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20%3D%3D%206%20%26%26%20name%7CindexOf(%22e%22)%3E0&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
755
- [21]:
756
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=array%5B1%5D%2B1&transforms=%7B%7D
757
- [22]:
758
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=object.name&transforms=%7B%7D
759
- [23]:
760
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%7Btype%3A%22Point%22%2Ccoordinates%3A%20%5Bvalue%2Cvalue%5D%7D&transforms=%7B%7D
761
- [99]:
762
- https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22text%22%20%3A%20%22%20%20foobar%7B%7D%20%20%22%0A%7D&input=text%20%7C%20replacestr(%22foo%22%2C%22FOO%22)%7Ctrim%7Curlencode&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D