@valkyriestudios/utils 12.33.1 → 12.35.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.
- package/README.md +208 -3
- package/array/index.d.ts +3 -1
- package/array/index.js +5 -1
- package/array/mapFn.d.ts +7 -4
- package/array/mapFn.js +7 -6
- package/array/mapFnAsMap.d.ts +38 -0
- package/array/mapFnAsMap.js +24 -0
- package/array/mapKey.d.ts +7 -4
- package/array/mapKey.js +11 -14
- package/array/mapKeyAsMap.d.ts +41 -0
- package/array/mapKeyAsMap.js +25 -0
- package/date/format.js +4 -1
- package/index.d.ts +82 -17
- package/modules/PubSub.js +1 -1
- package/modules/Scheduler.d.ts +139 -0
- package/modules/Scheduler.js +347 -0
- package/object/merge.d.ts +16 -12
- package/object/merge.js +2 -2
- package/package.json +41 -5
package/README.md
CHANGED
|
@@ -30,8 +30,12 @@ isNotEmptyArray([]); // FALSE
|
|
|
30
30
|
isNotEmptyArray([0, 1, 2]); // TRUE
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
### array/mapKey(val:Record[], key:string, opts?:{merge?:boolean;filter_fn?:(el:T) => boolean})
|
|
34
|
-
Map a non-primitive object array into an object map by key
|
|
33
|
+
### array/mapKey(val:Record[], key:string, opts?:{merge?:boolean;filter_fn?:(el:T) => boolean;transform_fn?:(el:T) => U})
|
|
34
|
+
Map a non-primitive object array into an object map by key.
|
|
35
|
+
|
|
36
|
+
**Take Note**: The function `array/mapKeyAsMap` has the same behavior as the mapKey function with the sole difference being that it returns a
|
|
37
|
+
**Map** instead of an object.
|
|
38
|
+
|
|
35
39
|
```typescript
|
|
36
40
|
import mapKey from '@valkyriestudios/utils/array/mapKey';
|
|
37
41
|
mapKey([
|
|
@@ -106,7 +110,7 @@ mapKey([
|
|
|
106
110
|
{name: 'Alana', isActive: true},
|
|
107
111
|
{uid: 87, name: 'Josh', isActive: false},
|
|
108
112
|
{uid: 12, name: 'Farah', isActive: false},
|
|
109
|
-
], 'uid', {merge: true})
|
|
113
|
+
], 'uid', {merge: true, filter_fn: el => el.isActive})
|
|
110
114
|
/* Expected output: */
|
|
111
115
|
{
|
|
112
116
|
12: {uid: 12, name: 'Peter', isActive: true},
|
|
@@ -114,6 +118,27 @@ mapKey([
|
|
|
114
118
|
}
|
|
115
119
|
```
|
|
116
120
|
|
|
121
|
+
also allows transforming objects at the same time with a custom transform_fn. Take Note that all of these operations are run in O(n):
|
|
122
|
+
```typescript
|
|
123
|
+
import mapKey from '@valkyriestudios/utils/array/mapKey';
|
|
124
|
+
mapKey([
|
|
125
|
+
{uid: 12, name: 'Peter', isActive: true},
|
|
126
|
+
{uid: 15, name: 'Jonas', dob: '2022-02-07', isActive: true},
|
|
127
|
+
{uid: 15, name: 'Bob', isActive: false},
|
|
128
|
+
{name: 'Alana', isActive: true},
|
|
129
|
+
{uid: 87, name: 'Josh', isActive: false},
|
|
130
|
+
{uid: 12, name: 'Farah', isActive: false},
|
|
131
|
+
], 'uid', {merge: true, filter_fn: el => el.isActive, transform_fn: el => pick(el, ["name"])})
|
|
132
|
+
/* Expected output: */
|
|
133
|
+
{
|
|
134
|
+
12: {name: 'Peter'},
|
|
135
|
+
15: {name: 'Jonas'},
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### array/mapKeyAsMap(val:Record[], key:string, opts?:{merge?:boolean;filter_fn?:(el:T) => boolean})
|
|
140
|
+
Same behavior as mapKey but returns a Map instead of an object
|
|
141
|
+
|
|
117
142
|
### array/mapFn(val:Record[], key:Function, opts:object={})
|
|
118
143
|
Same behavior as mapKey but instead of a key, a function is passed to generate your own key. Eg:
|
|
119
144
|
|
|
@@ -134,6 +159,9 @@ mapFn([
|
|
|
134
159
|
|
|
135
160
|
options are the same as the mapKey function
|
|
136
161
|
|
|
162
|
+
### array/mapFnAsMap(val:Record[], key:Function, opts:object={})
|
|
163
|
+
Same behavior as mapFn but returns a Map instead of an object
|
|
164
|
+
|
|
137
165
|
### array/mapPrimitive(val:any[], opts?:{valtrim:false;keyround:false;valround:false;filter_fn:(el)=>boolean})
|
|
138
166
|
Map an array of primitives (number/string)
|
|
139
167
|
```typescript
|
|
@@ -550,6 +578,7 @@ Format a date according to a spec/locale and zone
|
|
|
550
578
|
| `L` | Locale-Specific date | 15 jul 2024 |
|
|
551
579
|
| `t` | Locale-specific short time | 10:28 AM |
|
|
552
580
|
| `T` | Locale-specific time with seconds | 10:28:30 AM |
|
|
581
|
+
| `ISO` | Alias for `YYYY-MM-DD[T]HH:mm:ss.SSS[Z]` | 2025-03-16T14:24:59.010Z |
|
|
553
582
|
|
|
554
583
|
**Additional**:
|
|
555
584
|
Format has several additional functions defined which help usage inside of an ecosystem (eg: webapp) by **overriding the global defaults** used by format.
|
|
@@ -1101,6 +1130,182 @@ Both synchronous and asynchronous errors are logged via the custom logger (if pr
|
|
|
1101
1130
|
- **Flexibility:**
|
|
1102
1131
|
The storage behavior can be controlled globally through the constructor or overridden for individual publish calls.
|
|
1103
1132
|
|
|
1133
|
+
### modules/Scheduler
|
|
1134
|
+
A Lightweight scheduler module that works with a cron-like syntax and can be easily configured/run at runtime.
|
|
1135
|
+
|
|
1136
|
+
**Take Note**: This does **not replace** cron, it is a module that runs at runtime and could be used to simulate cron-like behavior, but it is **not a replacement**.
|
|
1137
|
+
|
|
1138
|
+
##### Usage
|
|
1139
|
+
Supports baseline operation:
|
|
1140
|
+
```typescript
|
|
1141
|
+
import { Scheduler } from '@valkyriestudios/utils/modules/PubSub';
|
|
1142
|
+
import * as Handlers from "..."; /* Example methods */
|
|
1143
|
+
|
|
1144
|
+
const mySchedule = new Scheduler({name: 'MyScheduler'});
|
|
1145
|
+
|
|
1146
|
+
mySchedule.add({schedule: '0 * * * *', name: 'send_emails', fn: Handlers.SendEmails});
|
|
1147
|
+
mySchedule.add({schedule: '0 */3 * * *', name: 'cleanup', fn: Handlers.Cleanup});
|
|
1148
|
+
mySchedule.add({schedule: '0,15,30,45 * * * *', name: 'synchronize', fn: Handlers.Synchronize});
|
|
1149
|
+
await mySchedule.run();
|
|
1150
|
+
```
|
|
1151
|
+
|
|
1152
|
+
Let's say you need to send something out in different timezones:
|
|
1153
|
+
```typescript
|
|
1154
|
+
import { Scheduler } from '@valkyriestudios/utils/modules/PubSub';
|
|
1155
|
+
import * as Handlers from "..."; /* Example methods */
|
|
1156
|
+
|
|
1157
|
+
...
|
|
1158
|
+
|
|
1159
|
+
const mySchedule = new Scheduler({name: 'MyScheduler'});
|
|
1160
|
+
|
|
1161
|
+
/* This is an example */
|
|
1162
|
+
for (const user of users) {
|
|
1163
|
+
mySchedule.add({
|
|
1164
|
+
schedule: '0 * * * *',
|
|
1165
|
+
name: 'send_emails',
|
|
1166
|
+
fn: Handlers.SendEmail,
|
|
1167
|
+
timeZone: user.timeZone, /* Given a user has a timezone */
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
await mySchedule.run();
|
|
1172
|
+
```
|
|
1173
|
+
|
|
1174
|
+
Too much flooding! let's turn off parallelization and have it run them in linear fashion:
|
|
1175
|
+
```typescript
|
|
1176
|
+
import { Scheduler } from '@valkyriestudios/utils/modules/PubSub';
|
|
1177
|
+
import * as Handlers from "..."; /* Example methods */
|
|
1178
|
+
|
|
1179
|
+
...
|
|
1180
|
+
|
|
1181
|
+
const mySchedule = new Scheduler({
|
|
1182
|
+
name: 'MyScheduler',
|
|
1183
|
+
parallel: false, /* By setting parallel to false we will ensure one at a time */
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
/* This is an example */
|
|
1187
|
+
for (const user of users) {
|
|
1188
|
+
mySchedule.add({
|
|
1189
|
+
schedule: '0 * * * *',
|
|
1190
|
+
name: 'send_emails',
|
|
1191
|
+
fn: Handlers.SendEmail,
|
|
1192
|
+
timeZone: user.timeZone, /* Given a user has a timezone */
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
await mySchedule.run();
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
Okay we can actually send 3 at a time, let's set that up:
|
|
1200
|
+
```typescript
|
|
1201
|
+
import { Scheduler } from '@valkyriestudios/utils/modules/PubSub';
|
|
1202
|
+
import * as Handlers from "..."; /* Example methods */
|
|
1203
|
+
|
|
1204
|
+
...
|
|
1205
|
+
|
|
1206
|
+
const mySchedule = new Scheduler({
|
|
1207
|
+
name: 'MyScheduler',
|
|
1208
|
+
parallel: 3, /* By setting parallel to a specific integer above 0 we will run X jobs in parallel at a time */
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
/* This is an example */
|
|
1212
|
+
for (const user of users) {
|
|
1213
|
+
mySchedule.add({
|
|
1214
|
+
schedule: '0 * * * *',
|
|
1215
|
+
name: 'send_emails',
|
|
1216
|
+
fn: Handlers.SendEmail,
|
|
1217
|
+
timeZone: user.timeZone, /* Given a user has a timezone */
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
await mySchedule.run();
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
Oh no the emails aren't going out to the right user because we didn't pass our data:
|
|
1225
|
+
```typescript
|
|
1226
|
+
import { Scheduler } from '@valkyriestudios/utils/modules/PubSub';
|
|
1227
|
+
import * as Handlers from "..."; /* Example methods */
|
|
1228
|
+
|
|
1229
|
+
...
|
|
1230
|
+
|
|
1231
|
+
const mySchedule = new Scheduler({
|
|
1232
|
+
name: 'MyScheduler',
|
|
1233
|
+
parallel: 3,
|
|
1234
|
+
});
|
|
1235
|
+
|
|
1236
|
+
/* This is an example */
|
|
1237
|
+
for (const user of users) {
|
|
1238
|
+
mySchedule.add({
|
|
1239
|
+
schedule: '0 * * * *',
|
|
1240
|
+
name: 'send_emails',
|
|
1241
|
+
fn: Handlers.SendEmail,
|
|
1242
|
+
timeZone: user.timeZone, /* Given a user has a timezone */
|
|
1243
|
+
data: user, /* You will have automatic type hinting on this with the first val of SendEmail handler */
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
await mySchedule.run();
|
|
1248
|
+
```
|
|
1249
|
+
|
|
1250
|
+
I want this to run continuously so that I can just leave it running on a server:
|
|
1251
|
+
```typescript
|
|
1252
|
+
import { Scheduler } from '@valkyriestudios/utils/modules/PubSub';
|
|
1253
|
+
import * as Handlers from "..."; /* Example methods */
|
|
1254
|
+
|
|
1255
|
+
...
|
|
1256
|
+
|
|
1257
|
+
const mySchedule = new Scheduler({
|
|
1258
|
+
name: 'MyScheduler',
|
|
1259
|
+
parallel: 3,
|
|
1260
|
+
auto: true, /* By enabling this the schedule will automatically check once per minute which ones it needs to run */
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
/* This is an example */
|
|
1264
|
+
for (const user of users) {
|
|
1265
|
+
mySchedule.add({
|
|
1266
|
+
schedule: '0 * * * *',
|
|
1267
|
+
name: 'send_emails',
|
|
1268
|
+
fn: Handlers.SendEmail,
|
|
1269
|
+
timeZone: user.timeZone,
|
|
1270
|
+
data: user,
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
await mySchedule.run();
|
|
1275
|
+
```
|
|
1276
|
+
|
|
1277
|
+
##### API Overview
|
|
1278
|
+
- **new Scheduler(options?: { logger?: LogFn; name?: string; timeZone?: string|null; parallel?:boolean|number; auto?: boolean })**
|
|
1279
|
+
Creates a new Scheduler instance.
|
|
1280
|
+
-- **logger (optional)**: Custom logging function to capture errors.
|
|
1281
|
+
-- **name (optional)**: A non‑empty string to name the instance.
|
|
1282
|
+
-- **timeZone (optional)**: (default=local timezone) The default timeZone the scheduler is run in
|
|
1283
|
+
-- **parallel (optional)**: (default=true) Scheduler will run jobs that need to run in parallel, set to false to run linear, set to a number to run X jobs in parallel
|
|
1284
|
+
-- **auto (optional)**: (default=false) Set to true to automatically run the schedule every 60 seconds
|
|
1285
|
+
- **add(job: {name:string; schedule:string; fn: Function; timeZone?: string | null; data?: unknown}): boolean**
|
|
1286
|
+
Add a job to the schedule
|
|
1287
|
+
-- If a timeZone is passed we will automatically check against local time in that timeZone on whether or not the job needs to run.
|
|
1288
|
+
-- If a data object is passed we will pass this data object to the provided function when run.
|
|
1289
|
+
-- Schedule uses cron-like schedule and is compatible with most of the standard cron formats.
|
|
1290
|
+
- **remove(name:string|string[]):void**
|
|
1291
|
+
Removes one or multiple jobs by name from the schedule.
|
|
1292
|
+
- **run(): Promise<void>**
|
|
1293
|
+
Runs the schedule.
|
|
1294
|
+
- **stopAutomaticRun():void**
|
|
1295
|
+
Stops automatic running (if enabled)
|
|
1296
|
+
- **startAutomaticRun():void**
|
|
1297
|
+
Start automatic running (if not enabled)
|
|
1298
|
+
- **static isCronSchedule(val:string):boolean**
|
|
1299
|
+
Returns true if the provided value is a valid cron schedule, false if it isnt
|
|
1300
|
+
- **static cronShouldRun(val:string, timeZone: string|null):boolean**
|
|
1301
|
+
Given a cron schedule and an optional timeZone returns whether or not the cron schedule should be run now
|
|
1302
|
+
|
|
1303
|
+
##### Notes:
|
|
1304
|
+
- **cron:**
|
|
1305
|
+
This is not a cron replacement but rather a module that can be used at runtime to handle cron-like schedules in a seamless way.
|
|
1306
|
+
- **auto:**
|
|
1307
|
+
If running this on a system that is replicated it **is highly advised** to not turn on automatic runs but rather build a way to trigger the schedule on a single instance into your networking.
|
|
1308
|
+
|
|
1104
1309
|
### number/is(val:unknown)
|
|
1105
1310
|
Check if a variable is a number
|
|
1106
1311
|
```typescript
|
package/array/index.d.ts
CHANGED
|
@@ -3,10 +3,12 @@ import { isArray } from './is';
|
|
|
3
3
|
import { isNotEmptyArray } from './isNotEmpty';
|
|
4
4
|
import { join } from './join';
|
|
5
5
|
import { mapFn } from './mapFn';
|
|
6
|
+
import { mapFnAsMap } from './mapFnAsMap';
|
|
6
7
|
import { mapKey } from './mapKey';
|
|
8
|
+
import { mapKeyAsMap } from './mapKeyAsMap';
|
|
7
9
|
import { mapPrimitive } from './mapPrimitive';
|
|
8
10
|
import { groupBy } from './groupBy';
|
|
9
11
|
import { shuffle } from './shuffle';
|
|
10
12
|
import { split } from './split';
|
|
11
13
|
import { sort } from './sort';
|
|
12
|
-
export { dedupe, isArray, isArray as is, isNotEmptyArray, isNotEmptyArray as isNotEmpty, isNotEmptyArray as isNeArray, isNotEmptyArray as isNe, join, mapFn, mapKey, mapPrimitive, groupBy, shuffle, split, sort };
|
|
14
|
+
export { dedupe, isArray, isArray as is, isNotEmptyArray, isNotEmptyArray as isNotEmpty, isNotEmptyArray as isNeArray, isNotEmptyArray as isNe, join, mapFn, mapFnAsMap, mapKey, mapKeyAsMap, mapPrimitive, groupBy, shuffle, split, sort };
|
package/array/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sort = exports.split = exports.shuffle = exports.groupBy = exports.mapPrimitive = exports.mapKey = exports.mapFn = exports.join = exports.isNe = exports.isNeArray = exports.isNotEmpty = exports.isNotEmptyArray = exports.is = exports.isArray = exports.dedupe = void 0;
|
|
3
|
+
exports.sort = exports.split = exports.shuffle = exports.groupBy = exports.mapPrimitive = exports.mapKeyAsMap = exports.mapKey = exports.mapFnAsMap = exports.mapFn = exports.join = exports.isNe = exports.isNeArray = exports.isNotEmpty = exports.isNotEmptyArray = exports.is = exports.isArray = exports.dedupe = void 0;
|
|
4
4
|
const dedupe_1 = require("./dedupe");
|
|
5
5
|
Object.defineProperty(exports, "dedupe", { enumerable: true, get: function () { return dedupe_1.dedupe; } });
|
|
6
6
|
const is_1 = require("./is");
|
|
@@ -15,8 +15,12 @@ const join_1 = require("./join");
|
|
|
15
15
|
Object.defineProperty(exports, "join", { enumerable: true, get: function () { return join_1.join; } });
|
|
16
16
|
const mapFn_1 = require("./mapFn");
|
|
17
17
|
Object.defineProperty(exports, "mapFn", { enumerable: true, get: function () { return mapFn_1.mapFn; } });
|
|
18
|
+
const mapFnAsMap_1 = require("./mapFnAsMap");
|
|
19
|
+
Object.defineProperty(exports, "mapFnAsMap", { enumerable: true, get: function () { return mapFnAsMap_1.mapFnAsMap; } });
|
|
18
20
|
const mapKey_1 = require("./mapKey");
|
|
19
21
|
Object.defineProperty(exports, "mapKey", { enumerable: true, get: function () { return mapKey_1.mapKey; } });
|
|
22
|
+
const mapKeyAsMap_1 = require("./mapKeyAsMap");
|
|
23
|
+
Object.defineProperty(exports, "mapKeyAsMap", { enumerable: true, get: function () { return mapKeyAsMap_1.mapKeyAsMap; } });
|
|
20
24
|
const mapPrimitive_1 = require("./mapPrimitive");
|
|
21
25
|
Object.defineProperty(exports, "mapPrimitive", { enumerable: true, get: function () { return mapPrimitive_1.mapPrimitive; } });
|
|
22
26
|
const groupBy_1 = require("./groupBy");
|
package/array/mapFn.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type MapOptions = {
|
|
1
|
+
type MapOptions<T, U = T> = {
|
|
2
2
|
/**
|
|
3
3
|
* Allow merging existing keys or not, if not keys will be overriden if they exist
|
|
4
4
|
* (default=false)
|
|
@@ -11,6 +11,11 @@ type MapOptions = {
|
|
|
11
11
|
* {12: {uid: 12, b: 'ho'}}
|
|
12
12
|
*/
|
|
13
13
|
merge?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* A custom transform function that is applied to each element before mapping.
|
|
16
|
+
* Its output type `U` becomes the value type of the resulting map.
|
|
17
|
+
*/
|
|
18
|
+
transform_fn?: (el: T) => U;
|
|
14
19
|
};
|
|
15
20
|
type MapFn<T extends Record<string, any>> = (entry: T) => (string | number | boolean);
|
|
16
21
|
/**
|
|
@@ -25,8 +30,6 @@ type MapFn<T extends Record<string, any>> = (entry: T) => (string | number | boo
|
|
|
25
30
|
* @param {Record<string, any>[]} val - Array to map
|
|
26
31
|
* @param {MapFn} fn - Handler function which is run for each of the objects and should return a string or number
|
|
27
32
|
* @param {MapOptions?} opts - Options object to override built-in defaults
|
|
28
|
-
*
|
|
29
|
-
* @returns {Record<string, T>} KV-Map object
|
|
30
33
|
*/
|
|
31
|
-
declare function mapFn<T extends Record<string, any
|
|
34
|
+
declare function mapFn<T extends Record<string, any>, U extends Record<string, any> = T>(arr: T[], fn: MapFn<T>, opts?: MapOptions<T, U>): Record<string, U>;
|
|
32
35
|
export { mapFn, mapFn as default };
|
package/array/mapFn.js
CHANGED
|
@@ -8,17 +8,18 @@ function mapFn(arr, fn, opts) {
|
|
|
8
8
|
typeof fn !== 'function')
|
|
9
9
|
return {};
|
|
10
10
|
const MERGE = opts?.merge === true;
|
|
11
|
+
const TRANSFORM_FN = opts?.transform_fn;
|
|
11
12
|
const map = {};
|
|
12
|
-
let hash = false;
|
|
13
13
|
for (let i = 0; i < arr.length; i++) {
|
|
14
14
|
const el = arr[i];
|
|
15
15
|
if (Object.prototype.toString.call(el) !== '[object Object]')
|
|
16
16
|
continue;
|
|
17
|
-
hash = fn(el);
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
let hash = fn(el);
|
|
18
|
+
if (Number.isFinite(hash) || (typeof hash === 'string' && hash.length)) {
|
|
19
|
+
hash = hash + '';
|
|
20
|
+
const transformed = TRANSFORM_FN ? TRANSFORM_FN(el) : el;
|
|
21
|
+
map[hash] = MERGE && hash in map ? (0, merge_1.merge)(map[hash], transformed, { union: true }) : transformed;
|
|
22
|
+
}
|
|
22
23
|
}
|
|
23
24
|
return map;
|
|
24
25
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
type MapOptions<T, U = T> = {
|
|
2
|
+
/**
|
|
3
|
+
* Allow merging existing keys or not, if not keys will be overriden if they exist
|
|
4
|
+
* (default=false)
|
|
5
|
+
*
|
|
6
|
+
* Example:
|
|
7
|
+
* mapFnAsMap([{uid: 12, a: 'hi'}, {uid: 12, b: 'ho'}], el => el.uid, {merge: true})
|
|
8
|
+
* Output:
|
|
9
|
+
* {12: {uid: 12, a: 'hi', b: 'ho'}}
|
|
10
|
+
* Output if merge is false
|
|
11
|
+
* {12: {uid: 12, b: 'ho'}}
|
|
12
|
+
*/
|
|
13
|
+
merge?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* A custom transform function that is applied to each element before mapping.
|
|
16
|
+
* Its output type `U` becomes the value type of the resulting map.
|
|
17
|
+
*/
|
|
18
|
+
transform_fn?: (el: T) => U;
|
|
19
|
+
};
|
|
20
|
+
type MapFn<T extends Record<string, any>> = (entry: T) => string | number | null;
|
|
21
|
+
/**
|
|
22
|
+
* Map an object array into a Map through a function that generates a key. Returning a non-string,
|
|
23
|
+
* non-numeric value from the function (eg: null) will filter out the object.
|
|
24
|
+
*
|
|
25
|
+
* Example:
|
|
26
|
+
* mapFnAsMap([{uid: 12, name: 'Peter'}, {uid: 15, name: 'Jonas'}], el => el.uid);
|
|
27
|
+
* Output:
|
|
28
|
+
* new Map([
|
|
29
|
+
* [12, {uid: 12, name: 'Peter'}],
|
|
30
|
+
* [15, {uid: 15, name: 'Jonas'}],
|
|
31
|
+
* ])
|
|
32
|
+
*
|
|
33
|
+
* @param {Record<string, any>[]} val - Array to map
|
|
34
|
+
* @param {MapFn} fn - Handler function which is run for each of the objects and should return a string or number
|
|
35
|
+
* @param {MapOptions?} opts - Options object to override built-in defaults
|
|
36
|
+
*/
|
|
37
|
+
declare function mapFnAsMap<T extends Record<string, any>, TFN extends MapFn<T>, U extends Record<string, any> = T>(arr: T[], fn: TFN, opts?: MapOptions<T, U>): Map<NonNullable<ReturnType<TFN>>, U>;
|
|
38
|
+
export { mapFnAsMap, mapFnAsMap as default };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapFnAsMap = mapFnAsMap;
|
|
4
|
+
exports.default = mapFnAsMap;
|
|
5
|
+
const merge_1 = require("../object/merge");
|
|
6
|
+
function mapFnAsMap(arr, fn, opts) {
|
|
7
|
+
if ((!Array.isArray(arr) || !arr.length) ||
|
|
8
|
+
typeof fn !== 'function')
|
|
9
|
+
return new Map();
|
|
10
|
+
const MERGE = opts?.merge === true;
|
|
11
|
+
const TRANSFORM_FN = opts?.transform_fn;
|
|
12
|
+
const map = new Map();
|
|
13
|
+
for (let i = 0; i < arr.length; i++) {
|
|
14
|
+
const el = arr[i];
|
|
15
|
+
if (Object.prototype.toString.call(el) !== '[object Object]')
|
|
16
|
+
continue;
|
|
17
|
+
const hash = fn(el);
|
|
18
|
+
if (Number.isFinite(hash) || (typeof hash === 'string' && hash.trim().length)) {
|
|
19
|
+
const transformed = TRANSFORM_FN ? TRANSFORM_FN(el) : el;
|
|
20
|
+
map.set(hash, MERGE && map.has(hash) ? (0, merge_1.merge)(map.get(hash), transformed, { union: true }) : transformed);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return map;
|
|
24
|
+
}
|
package/array/mapKey.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type MapOptions<T> = {
|
|
1
|
+
type MapOptions<T, U = T> = {
|
|
2
2
|
/**
|
|
3
3
|
* Allow merging existing keys or not, if not keys will be overriden if they exist
|
|
4
4
|
* (default=false)
|
|
@@ -15,6 +15,11 @@ type MapOptions<T> = {
|
|
|
15
15
|
* Pass a custom filter function which will be run in O(n) while iterating
|
|
16
16
|
*/
|
|
17
17
|
filter_fn?: (el: T) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* A custom transformer function to modify each element before mapping.
|
|
20
|
+
* Its output type `U` becomes the value type of the resulting map.
|
|
21
|
+
*/
|
|
22
|
+
transform_fn?: (el: T) => U;
|
|
18
23
|
};
|
|
19
24
|
/**
|
|
20
25
|
* Map an object array into a kv-object by passing a common key that exists on the objects. Objects for
|
|
@@ -28,8 +33,6 @@ type MapOptions<T> = {
|
|
|
28
33
|
* @param {Record<string,any>[]} val - Array to map
|
|
29
34
|
* @param {string} key - Key to map by
|
|
30
35
|
* @param {MapOptions?} opts - Options object to override built-in defaults
|
|
31
|
-
*
|
|
32
|
-
* @returns {Record<string, T>} KV-Map object
|
|
33
36
|
*/
|
|
34
|
-
declare function mapKey<T extends Record<string, any
|
|
37
|
+
declare function mapKey<T extends Record<string, any>, U extends Record<string, any> = T, TKey extends keyof T = string>(arr: T[], key: TKey, opts?: MapOptions<T, U>): Record<string, U>;
|
|
35
38
|
export { mapKey, mapKey as default };
|
package/array/mapKey.js
CHANGED
|
@@ -4,25 +4,22 @@ exports.mapKey = mapKey;
|
|
|
4
4
|
exports.default = mapKey;
|
|
5
5
|
const merge_1 = require("../object/merge");
|
|
6
6
|
function mapKey(arr, key, opts) {
|
|
7
|
-
if (!Array.isArray(arr) ||
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return {};
|
|
12
|
-
const key_s = key.trim();
|
|
13
|
-
if (!key_s.length)
|
|
7
|
+
if (!Array.isArray(arr) ||
|
|
8
|
+
!arr.length ||
|
|
9
|
+
typeof key !== 'string' ||
|
|
10
|
+
!key.length)
|
|
14
11
|
return {};
|
|
15
12
|
const FILTER_FN = opts?.filter_fn;
|
|
16
13
|
const MERGE = opts?.merge === true;
|
|
14
|
+
const TRANSFORMER = opts?.transform_fn;
|
|
17
15
|
const map = {};
|
|
18
|
-
for (let i = 0; i <
|
|
16
|
+
for (let i = 0; i < arr.length; i++) {
|
|
19
17
|
const el = arr[i];
|
|
20
|
-
const el_key = el?.[
|
|
21
|
-
if (el_key
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
map[el_key] = (MERGE && el_key in map ? (0, merge_1.merge)(map[el_key], el, { union: true }) : el);
|
|
18
|
+
const el_key = el?.[key];
|
|
19
|
+
if (el_key !== undefined && (!FILTER_FN || FILTER_FN(el))) {
|
|
20
|
+
const transformed = TRANSFORMER ? TRANSFORMER(el) : el;
|
|
21
|
+
map[el_key] = MERGE && el_key in map ? (0, merge_1.merge)(map[el_key], transformed, { union: true }) : transformed;
|
|
22
|
+
}
|
|
26
23
|
}
|
|
27
24
|
return map;
|
|
28
25
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
type MapOptions<T, U = T> = {
|
|
2
|
+
/**
|
|
3
|
+
* Allow merging existing keys or not, if not keys will be overriden if they exist
|
|
4
|
+
* (default=false)
|
|
5
|
+
*
|
|
6
|
+
* Example:
|
|
7
|
+
* mapKey([{uid: 12, a: 'hi'}, {uid: 12, b: 'ho'}], 'uid', {merge: true})
|
|
8
|
+
* Output:
|
|
9
|
+
* {12: {uid: 12, a: 'hi', b: 'ho'}}
|
|
10
|
+
* Output if merge is false
|
|
11
|
+
* {12: {uid: 12, b: 'ho'}}
|
|
12
|
+
*/
|
|
13
|
+
merge?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Pass a custom filter function which will be run in O(n) while iterating
|
|
16
|
+
*/
|
|
17
|
+
filter_fn?: (el: T) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* A custom transformer function to modify each element before mapping.
|
|
20
|
+
* Its output type `U` becomes the value type of the resulting map.
|
|
21
|
+
*/
|
|
22
|
+
transform_fn?: (el: T) => U;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Map an object array into a Map by passing a common key that exists on the objects. Objects for
|
|
26
|
+
* which the key doesn't exist will be filtered out automatically
|
|
27
|
+
*
|
|
28
|
+
* Example:
|
|
29
|
+
* mapKey([{uid: 12, name: 'Peter'}, {uid: 15, name: 'Jonas'}], 'uid');
|
|
30
|
+
* Output:
|
|
31
|
+
* new Map([
|
|
32
|
+
* [12, {uid: 12, name: 'Peter'}],
|
|
33
|
+
* [15, {uid: 15, name: 'Jonas'}],
|
|
34
|
+
* ])
|
|
35
|
+
*
|
|
36
|
+
* @param {Record<string,any>[]} val - Array to map
|
|
37
|
+
* @param {string} key - Key to map by
|
|
38
|
+
* @param {MapOptions?} opts - Options object to override built-in defaults
|
|
39
|
+
*/
|
|
40
|
+
declare function mapKeyAsMap<T extends Record<string, any>, U extends Record<string, any> = T, TKey extends keyof T = string>(arr: T[], key: TKey, opts?: MapOptions<T, U>): Map<T[TKey], U>;
|
|
41
|
+
export { mapKeyAsMap, mapKeyAsMap as default };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapKeyAsMap = mapKeyAsMap;
|
|
4
|
+
exports.default = mapKeyAsMap;
|
|
5
|
+
const merge_1 = require("../object/merge");
|
|
6
|
+
function mapKeyAsMap(arr, key, opts) {
|
|
7
|
+
if (!Array.isArray(arr) ||
|
|
8
|
+
!arr.length ||
|
|
9
|
+
typeof key !== 'string' ||
|
|
10
|
+
!key.length)
|
|
11
|
+
return new Map();
|
|
12
|
+
const FILTER_FN = opts?.filter_fn;
|
|
13
|
+
const MERGE = opts?.merge === true;
|
|
14
|
+
const TRANSFORMER = opts?.transform_fn;
|
|
15
|
+
const map = new Map();
|
|
16
|
+
for (let i = 0; i < arr.length; i++) {
|
|
17
|
+
const el = arr[i];
|
|
18
|
+
const el_key = el?.[key];
|
|
19
|
+
if (el_key !== undefined && (!FILTER_FN || FILTER_FN(el))) {
|
|
20
|
+
const transformed = (TRANSFORMER ? TRANSFORMER(el) : el);
|
|
21
|
+
map.set(el_key, MERGE && map.has(el_key) ? (0, merge_1.merge)(map.get(el_key), transformed, { union: true }) : transformed);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return map;
|
|
25
|
+
}
|
package/date/format.js
CHANGED
|
@@ -178,6 +178,9 @@ function getSpecChain(spec) {
|
|
|
178
178
|
spec_cache.set(spec, result);
|
|
179
179
|
return result;
|
|
180
180
|
}
|
|
181
|
+
const SPEC_ALIASES = {
|
|
182
|
+
ISO: 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]',
|
|
183
|
+
};
|
|
181
184
|
function format(val, spec, locale = DEFAULT_LOCALE, zone = DEFAULT_TZ, sow = DEFAULT_SOW) {
|
|
182
185
|
const n_val = (0, convertToDate_1.convertToDate)(val);
|
|
183
186
|
if (n_val === null)
|
|
@@ -188,7 +191,7 @@ function format(val, spec, locale = DEFAULT_LOCALE, zone = DEFAULT_TZ, sow = DEF
|
|
|
188
191
|
throw new TypeError('format: locale must be a string');
|
|
189
192
|
if (typeof zone !== 'string')
|
|
190
193
|
throw new TypeError('format: zone must be a string');
|
|
191
|
-
const n_spec = getSpecChain(spec);
|
|
194
|
+
const n_spec = getSpecChain(SPEC_ALIASES[spec] || spec);
|
|
192
195
|
if (!n_spec)
|
|
193
196
|
return n_val.toISOString();
|
|
194
197
|
const d = toZone(n_val, zone);
|