@ix-xs/node-comfort 1.1.31 → 1.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.
- package/LICENSE +21 -0
- package/README.md +623 -748
- package/index.js +39 -13
- package/package.json +74 -21
- package/src/Arr.js +433 -0
- package/src/Checker.js +547 -188
- package/src/FS.js +127 -1
- package/src/Func.js +375 -0
- package/src/Id.js +235 -0
- package/src/Logger.js +71 -0
- package/src/Num.js +366 -0
- package/src/Obj.js +421 -0
- package/src/SQLite.js +20 -9
- package/src/Str.js +452 -0
- package/src/Time.js +485 -0
- package/types/index.d.ts +406 -0
- package/types/index.d.ts.map +1 -1
- package/types/src/Arr.d.ts +252 -0
- package/types/src/Arr.d.ts.map +1 -0
- package/types/src/Checker.d.ts +292 -30
- package/types/src/Checker.d.ts.map +1 -1
- package/types/src/FS.d.ts +60 -0
- package/types/src/FS.d.ts.map +1 -1
- package/types/src/Func.d.ts +210 -0
- package/types/src/Func.d.ts.map +1 -0
- package/types/src/Id.d.ts +119 -0
- package/types/src/Id.d.ts.map +1 -0
- package/types/src/Logger.d.ts +33 -0
- package/types/src/Logger.d.ts.map +1 -1
- package/types/src/Num.d.ts +242 -0
- package/types/src/Num.d.ts.map +1 -0
- package/types/src/Obj.d.ts +189 -0
- package/types/src/Obj.d.ts.map +1 -0
- package/types/src/SQLite.d.ts +6 -1
- package/types/src/SQLite.d.ts.map +1 -1
- package/types/src/Str.d.ts +293 -0
- package/types/src/Str.d.ts.map +1 -0
- package/types/src/Time.d.ts +205 -0
- package/types/src/Time.d.ts.map +1 -0
- package/tsconfig.json +0 -18
package/index.js
CHANGED
|
@@ -1,13 +1,39 @@
|
|
|
1
|
-
const Logger = require("./src/Logger");
|
|
2
|
-
const FS = require("./src/FS");
|
|
3
|
-
const Checker = require("./src/Checker");
|
|
4
|
-
const Utils = require("./src/Utils");
|
|
5
|
-
const SQLite = require("./src/SQLite");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
const Logger = require("./src/Logger");
|
|
2
|
+
const FS = require("./src/FS");
|
|
3
|
+
const Checker = require("./src/Checker");
|
|
4
|
+
const Utils = require("./src/Utils");
|
|
5
|
+
const SQLite = require("./src/SQLite");
|
|
6
|
+
const str = require("./src/Str");
|
|
7
|
+
const num = require("./src/Num");
|
|
8
|
+
const arr = require("./src/Arr");
|
|
9
|
+
const obj = require("./src/Obj");
|
|
10
|
+
const func = require("./src/Func");
|
|
11
|
+
const time = require("./src/Time");
|
|
12
|
+
const id = require("./src/Id");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @ix-xs/node-comfort
|
|
16
|
+
*
|
|
17
|
+
* A zero-dependency comfort belt for Node.js: a colorful logger, a safe
|
|
18
|
+
* filesystem helper, a tiny SQLite wrapper, runtime type checks, async/process
|
|
19
|
+
* helpers, plus rich string, number, array, object, function, date and
|
|
20
|
+
* id/crypto utilities.
|
|
21
|
+
*/
|
|
22
|
+
module.exports = {
|
|
23
|
+
...Logger,
|
|
24
|
+
...FS,
|
|
25
|
+
...Checker,
|
|
26
|
+
...Utils,
|
|
27
|
+
SQLite,
|
|
28
|
+
str,
|
|
29
|
+
num,
|
|
30
|
+
arr,
|
|
31
|
+
obj,
|
|
32
|
+
func,
|
|
33
|
+
time,
|
|
34
|
+
id,
|
|
35
|
+
logger: Logger,
|
|
36
|
+
fs: FS,
|
|
37
|
+
checker: Checker,
|
|
38
|
+
utils: Utils,
|
|
39
|
+
};
|
package/package.json
CHANGED
|
@@ -1,21 +1,74 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"types": "types/index.d.ts",
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@ix-xs/node-comfort",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Zero-dependency comfort belt for Node.js: colorful logger, filesystem helper, SQLite wrapper, runtime type checks, async/process helpers, plus rich string, number, array, object, function, date and id/crypto utilities.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "types/index.d.ts",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"utils",
|
|
10
|
+
"utilities",
|
|
11
|
+
"helpers",
|
|
12
|
+
"toolkit",
|
|
13
|
+
"logger",
|
|
14
|
+
"console",
|
|
15
|
+
"colors",
|
|
16
|
+
"ansi",
|
|
17
|
+
"filesystem",
|
|
18
|
+
"fs",
|
|
19
|
+
"sqlite",
|
|
20
|
+
"database",
|
|
21
|
+
"type-check",
|
|
22
|
+
"guards",
|
|
23
|
+
"validation",
|
|
24
|
+
"string",
|
|
25
|
+
"slugify",
|
|
26
|
+
"camelcase",
|
|
27
|
+
"number",
|
|
28
|
+
"format-bytes",
|
|
29
|
+
"array",
|
|
30
|
+
"object",
|
|
31
|
+
"deep-clone",
|
|
32
|
+
"deep-merge",
|
|
33
|
+
"debounce",
|
|
34
|
+
"throttle",
|
|
35
|
+
"memoize",
|
|
36
|
+
"retry",
|
|
37
|
+
"date",
|
|
38
|
+
"duration",
|
|
39
|
+
"uuid",
|
|
40
|
+
"ulid",
|
|
41
|
+
"nanoid",
|
|
42
|
+
"hash",
|
|
43
|
+
"crypto",
|
|
44
|
+
"zero-dependency",
|
|
45
|
+
"no-dependencies"
|
|
46
|
+
],
|
|
47
|
+
"author": "ix-xs",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=20.12.0"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"index.js",
|
|
54
|
+
"src/",
|
|
55
|
+
"types/",
|
|
56
|
+
"README.md",
|
|
57
|
+
"LICENSE"
|
|
58
|
+
],
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsc",
|
|
61
|
+
"prepublishOnly": "npm run build"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"typescript": "^5.6.0"
|
|
65
|
+
},
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "https://github.com/ix-xs/node-comfort.git"
|
|
69
|
+
},
|
|
70
|
+
"bugs": {
|
|
71
|
+
"url": "https://github.com/ix-xs/node-comfort/issues"
|
|
72
|
+
},
|
|
73
|
+
"homepage": "https://github.com/ix-xs/node-comfort#readme"
|
|
74
|
+
}
|
package/src/Arr.js
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Array utilities.
|
|
3
|
+
*
|
|
4
|
+
* Chunking, grouping, uniqueness, set operations, shuffling, flattening and
|
|
5
|
+
* more • expressed as small pure functions that never mutate their input.
|
|
6
|
+
*
|
|
7
|
+
* Many methods accept an `iteratee`: either a property key (string) or a
|
|
8
|
+
* function mapping an item to a value.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Turns a key or function into a value-extracting function.
|
|
13
|
+
* @private
|
|
14
|
+
* @param {string|((item: any) => any)} [iteratee]
|
|
15
|
+
* @returns {(item: any) => any}
|
|
16
|
+
*/
|
|
17
|
+
const _iteratee = (iteratee) => {
|
|
18
|
+
if (typeof iteratee === "function") return iteratee;
|
|
19
|
+
if (typeof iteratee === "string") return (item) => (item == null ? undefined : item[iteratee]);
|
|
20
|
+
return (item) => item;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
/**
|
|
25
|
+
* Splits an array into chunks of a given size.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* nodeComfort.arr.chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
|
|
29
|
+
*
|
|
30
|
+
* @param {any[]} array - Source array.
|
|
31
|
+
* @param {number} size - Chunk size (must be >= 1).
|
|
32
|
+
* @returns {any[][]} The chunked array.
|
|
33
|
+
*/
|
|
34
|
+
chunk(array, size) {
|
|
35
|
+
if (size < 1) return [];
|
|
36
|
+
const result = [];
|
|
37
|
+
for (let i = 0; i < array.length; i += size) {
|
|
38
|
+
result.push(array.slice(i, i + size));
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Returns a new array with duplicate values removed.
|
|
45
|
+
*
|
|
46
|
+
* Pass an `iteratee` to de-duplicate by a derived key.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* nodeComfort.arr.unique([1, 1, 2, 3]); // [1, 2, 3]
|
|
50
|
+
* nodeComfort.arr.unique([{ id: 1 }, { id: 1 }], "id"); // [{ id: 1 }]
|
|
51
|
+
*
|
|
52
|
+
* @param {any[]} array - Source array.
|
|
53
|
+
* @param {string|((item: any) => any)} [iteratee] - Key or function to compare by.
|
|
54
|
+
* @returns {any[]} The de-duplicated array.
|
|
55
|
+
*/
|
|
56
|
+
unique(array, iteratee) {
|
|
57
|
+
if (!iteratee) return [...new Set(array)];
|
|
58
|
+
const fn = _iteratee(iteratee);
|
|
59
|
+
const seen = new Set();
|
|
60
|
+
const result = [];
|
|
61
|
+
for (const item of array) {
|
|
62
|
+
const key = fn(item);
|
|
63
|
+
if (!seen.has(key)) {
|
|
64
|
+
seen.add(key);
|
|
65
|
+
result.push(item);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Groups the items of an array by a key or function.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* nodeComfort.arr.groupBy([1, 2, 3, 4], (n) => (n % 2 ? "odd" : "even"));
|
|
76
|
+
* // { odd: [1, 3], even: [2, 4] }
|
|
77
|
+
*
|
|
78
|
+
* @param {any[]} array - Source array.
|
|
79
|
+
* @param {string|((item: any) => any)} iteratee - Key or function to group by.
|
|
80
|
+
* @returns {Record<string, any[]>} The grouped object.
|
|
81
|
+
*/
|
|
82
|
+
groupBy(array, iteratee) {
|
|
83
|
+
const fn = _iteratee(iteratee);
|
|
84
|
+
const result = {};
|
|
85
|
+
for (const item of array) {
|
|
86
|
+
const key = fn(item);
|
|
87
|
+
(result[key] ??= []).push(item);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Builds an object keyed by a derived value, mapping each key to the
|
|
94
|
+
* (last) matching item.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* nodeComfort.arr.keyBy([{ id: "a" }, { id: "b" }], "id");
|
|
98
|
+
* // { a: { id: "a" }, b: { id: "b" } }
|
|
99
|
+
*
|
|
100
|
+
* @param {any[]} array - Source array.
|
|
101
|
+
* @param {string|((item: any) => any)} iteratee - Key or function.
|
|
102
|
+
* @returns {Record<string, any>} The keyed object.
|
|
103
|
+
*/
|
|
104
|
+
keyBy(array, iteratee) {
|
|
105
|
+
const fn = _iteratee(iteratee);
|
|
106
|
+
const result = {};
|
|
107
|
+
for (const item of array) result[fn(item)] = item;
|
|
108
|
+
return result;
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Partitions an array into two groups based on a predicate:
|
|
113
|
+
* `[matching, notMatching]`.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* nodeComfort.arr.partition([1, 2, 3, 4], (n) => n % 2 === 0);
|
|
117
|
+
* // [[2, 4], [1, 3]]
|
|
118
|
+
*
|
|
119
|
+
* @param {any[]} array - Source array.
|
|
120
|
+
* @param {(item: any, index: number) => boolean} predicate - Test function.
|
|
121
|
+
* @returns {[any[], any[]]} The two partitions.
|
|
122
|
+
*/
|
|
123
|
+
partition(array, predicate) {
|
|
124
|
+
const pass = [];
|
|
125
|
+
const fail = [];
|
|
126
|
+
array.forEach((item, i) => (predicate(item, i) ? pass : fail).push(item));
|
|
127
|
+
return [pass, fail];
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Recursively flattens a nested array up to a given depth.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* nodeComfort.arr.flatten([1, [2, [3, [4]]]]); // [1, 2, 3, [4]]
|
|
135
|
+
* nodeComfort.arr.flatten([1, [2, [3]]], Infinity); // [1, 2, 3]
|
|
136
|
+
*
|
|
137
|
+
* @param {any[]} array - Source array.
|
|
138
|
+
* @param {number} [depth=1] - Maximum depth to flatten.
|
|
139
|
+
* @returns {any[]} The flattened array.
|
|
140
|
+
*/
|
|
141
|
+
flatten(array, depth = 1) {
|
|
142
|
+
return array.flat(depth);
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Returns a shuffled copy of an array (Fisher–Yates).
|
|
147
|
+
*
|
|
148
|
+
* Uses `Math.random` • not cryptographically secure.
|
|
149
|
+
*
|
|
150
|
+
* @param {any[]} array - Source array.
|
|
151
|
+
* @returns {any[]} A new shuffled array.
|
|
152
|
+
*/
|
|
153
|
+
shuffle(array) {
|
|
154
|
+
const result = [...array];
|
|
155
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
156
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
157
|
+
[result[i], result[j]] = [result[j], result[i]];
|
|
158
|
+
}
|
|
159
|
+
return result;
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Returns a random element from an array.
|
|
164
|
+
*
|
|
165
|
+
* @param {any[]} array - Source array.
|
|
166
|
+
* @returns {any} A random element, or undefined if empty.
|
|
167
|
+
*/
|
|
168
|
+
sample(array) {
|
|
169
|
+
return array.length ? array[Math.floor(Math.random() * array.length)] : undefined;
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Returns `n` random elements from an array (without repetition).
|
|
174
|
+
*
|
|
175
|
+
* @param {any[]} array - Source array.
|
|
176
|
+
* @param {number} n - Number of elements to pick.
|
|
177
|
+
* @returns {any[]} The sampled elements.
|
|
178
|
+
*/
|
|
179
|
+
sampleSize(array, n) {
|
|
180
|
+
return this.shuffle(array).slice(0, Math.max(0, n));
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Sorts an array by one or more iteratees, ascending by default.
|
|
185
|
+
*
|
|
186
|
+
* Does not mutate the input.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* nodeComfort.arr.sortBy(users, "age");
|
|
190
|
+
* nodeComfort.arr.sortBy(users, [(u) => u.age, "name"]);
|
|
191
|
+
*
|
|
192
|
+
* @param {any[]} array - Source array.
|
|
193
|
+
* @param {string|((item: any) => any)|Array<string|((item: any) => any)>} iteratees - Sort key(s).
|
|
194
|
+
* @param {"asc"|"desc"} [direction="asc"] - Sort direction.
|
|
195
|
+
* @returns {any[]} A new sorted array.
|
|
196
|
+
*/
|
|
197
|
+
sortBy(array, iteratees, direction = "asc") {
|
|
198
|
+
const fns = (Array.isArray(iteratees) ? iteratees : [iteratees]).map(_iteratee);
|
|
199
|
+
const dir = direction === "desc" ? -1 : 1;
|
|
200
|
+
return [...array].sort((a, b) => {
|
|
201
|
+
for (const fn of fns) {
|
|
202
|
+
const va = fn(a);
|
|
203
|
+
const vb = fn(b);
|
|
204
|
+
if (va < vb) return -1 * dir;
|
|
205
|
+
if (va > vb) return 1 * dir;
|
|
206
|
+
}
|
|
207
|
+
return 0;
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Returns the elements present in the first array but not in the others.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* nodeComfort.arr.difference([1, 2, 3, 4], [2, 4]); // [1, 3]
|
|
216
|
+
*
|
|
217
|
+
* @param {any[]} array - Source array.
|
|
218
|
+
* @param {...any[]} others - Arrays to subtract.
|
|
219
|
+
* @returns {any[]} The difference.
|
|
220
|
+
*/
|
|
221
|
+
difference(array, ...others) {
|
|
222
|
+
const exclude = new Set(others.flat());
|
|
223
|
+
return array.filter((item) => !exclude.has(item));
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Returns the elements common to all provided arrays.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* nodeComfort.arr.intersection([1, 2, 3], [2, 3, 4]); // [2, 3]
|
|
231
|
+
*
|
|
232
|
+
* @param {...any[]} arrays - Arrays to intersect.
|
|
233
|
+
* @returns {any[]} The intersection.
|
|
234
|
+
*/
|
|
235
|
+
intersection(...arrays) {
|
|
236
|
+
if (!arrays.length) return [];
|
|
237
|
+
const [first, ...rest] = arrays;
|
|
238
|
+
const sets = rest.map((a) => new Set(a));
|
|
239
|
+
return [...new Set(first)].filter((item) => sets.every((set) => set.has(item)));
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Returns the union (unique values) of all provided arrays.
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* nodeComfort.arr.union([1, 2], [2, 3], [3, 4]); // [1, 2, 3, 4]
|
|
247
|
+
*
|
|
248
|
+
* @param {...any[]} arrays - Arrays to unite.
|
|
249
|
+
* @returns {any[]} The union.
|
|
250
|
+
*/
|
|
251
|
+
union(...arrays) {
|
|
252
|
+
return [...new Set(arrays.flat())];
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Zips multiple arrays together into an array of tuples.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* nodeComfort.arr.zip(["a", "b"], [1, 2]); // [["a", 1], ["b", 2]]
|
|
260
|
+
*
|
|
261
|
+
* @param {...any[]} arrays - Arrays to zip.
|
|
262
|
+
* @returns {any[][]} The zipped tuples.
|
|
263
|
+
*/
|
|
264
|
+
zip(...arrays) {
|
|
265
|
+
const length = Math.max(0, ...arrays.map((a) => a.length));
|
|
266
|
+
const result = [];
|
|
267
|
+
for (let i = 0; i < length; i++) {
|
|
268
|
+
result.push(arrays.map((a) => a[i]));
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Splits an array into `matching` and removes falsy values.
|
|
275
|
+
*
|
|
276
|
+
* Removes `false`, `null`, `0`, `""`, `undefined` and `NaN`.
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* nodeComfort.arr.compact([0, 1, false, 2, "", 3, null]); // [1, 2, 3]
|
|
280
|
+
*
|
|
281
|
+
* @param {any[]} array - Source array.
|
|
282
|
+
* @returns {any[]} The compacted array.
|
|
283
|
+
*/
|
|
284
|
+
compact(array) {
|
|
285
|
+
return array.filter(Boolean);
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Returns the first element (or the first `n` elements) of an array.
|
|
290
|
+
*
|
|
291
|
+
* @param {any[]} array - Source array.
|
|
292
|
+
* @param {number} [n] - How many elements to take.
|
|
293
|
+
* @returns {any} The first element, or the first `n` elements when `n` is given.
|
|
294
|
+
*/
|
|
295
|
+
first(array, n) {
|
|
296
|
+
if (n === undefined) return array[0];
|
|
297
|
+
return array.slice(0, Math.max(0, n));
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Returns the last element (or the last `n` elements) of an array.
|
|
302
|
+
*
|
|
303
|
+
* @param {any[]} array - Source array.
|
|
304
|
+
* @param {number} [n] - How many elements to take.
|
|
305
|
+
* @returns {any} The last element, or the last `n` elements when `n` is given.
|
|
306
|
+
*/
|
|
307
|
+
last(array, n) {
|
|
308
|
+
if (n === undefined) return array[array.length - 1];
|
|
309
|
+
return array.slice(Math.max(0, array.length - n));
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Removes items from an array where the predicate is true, returning a new array.
|
|
314
|
+
*
|
|
315
|
+
* @param {any[]} array - Source array.
|
|
316
|
+
* @param {(item: any, index: number) => boolean} predicate - Removal test.
|
|
317
|
+
* @returns {any[]} A new array without the removed items.
|
|
318
|
+
*/
|
|
319
|
+
remove(array, predicate) {
|
|
320
|
+
return array.filter((item, i) => !predicate(item, i));
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Moves an element from one index to another (returns a new array).
|
|
325
|
+
*
|
|
326
|
+
* @param {any[]} array - Source array.
|
|
327
|
+
* @param {number} from - Source index.
|
|
328
|
+
* @param {number} to - Destination index.
|
|
329
|
+
* @returns {any[]} The reordered array.
|
|
330
|
+
*/
|
|
331
|
+
move(array, from, to) {
|
|
332
|
+
const result = [...array];
|
|
333
|
+
const [item] = result.splice(from, 1);
|
|
334
|
+
result.splice(to, 0, item);
|
|
335
|
+
return result;
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Counts occurrences of each derived key in an array.
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* nodeComfort.arr.countBy(["a", "b", "a"]); // { a: 2, b: 1 }
|
|
343
|
+
* nodeComfort.arr.countBy([1.1, 2.3, 1.7], Math.floor); // { 1: 2, 2: 1 }
|
|
344
|
+
*
|
|
345
|
+
* @param {any[]} array - Source array.
|
|
346
|
+
* @param {string|((item: any) => any)} [iteratee] - Key or function.
|
|
347
|
+
* @returns {Record<string, number>} The counts.
|
|
348
|
+
*/
|
|
349
|
+
countBy(array, iteratee) {
|
|
350
|
+
const fn = _iteratee(iteratee);
|
|
351
|
+
const result = {};
|
|
352
|
+
for (const item of array) {
|
|
353
|
+
const key = fn(item);
|
|
354
|
+
result[key] = (result[key] ?? 0) + 1;
|
|
355
|
+
}
|
|
356
|
+
return result;
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Sums an array by a numeric iteratee (or the values themselves).
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* nodeComfort.arr.sumBy([{ n: 1 }, { n: 2 }], "n"); // 3
|
|
364
|
+
*
|
|
365
|
+
* @param {any[]} array - Source array.
|
|
366
|
+
* @param {string|((item: any) => number)} [iteratee] - Key or function returning a number.
|
|
367
|
+
* @returns {number} The sum.
|
|
368
|
+
*/
|
|
369
|
+
sumBy(array, iteratee) {
|
|
370
|
+
const fn = _iteratee(iteratee);
|
|
371
|
+
return array.reduce((acc, item) => acc + (Number(fn(item)) || 0), 0);
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Returns the item with the maximum derived value.
|
|
376
|
+
*
|
|
377
|
+
* @param {any[]} array - Source array.
|
|
378
|
+
* @param {string|((item: any) => number)} [iteratee] - Key or function.
|
|
379
|
+
* @returns {any} The item, or undefined if the array is empty.
|
|
380
|
+
*/
|
|
381
|
+
maxBy(array, iteratee) {
|
|
382
|
+
const fn = _iteratee(iteratee);
|
|
383
|
+
let best;
|
|
384
|
+
let bestVal = -Infinity;
|
|
385
|
+
for (const item of array) {
|
|
386
|
+
const val = fn(item);
|
|
387
|
+
if (val > bestVal) {
|
|
388
|
+
bestVal = val;
|
|
389
|
+
best = item;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return best;
|
|
393
|
+
},
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Returns the item with the minimum derived value.
|
|
397
|
+
*
|
|
398
|
+
* @param {any[]} array - Source array.
|
|
399
|
+
* @param {string|((item: any) => number)} [iteratee] - Key or function.
|
|
400
|
+
* @returns {any} The item, or undefined if the array is empty.
|
|
401
|
+
*/
|
|
402
|
+
minBy(array, iteratee) {
|
|
403
|
+
const fn = _iteratee(iteratee);
|
|
404
|
+
let best;
|
|
405
|
+
let bestVal = Infinity;
|
|
406
|
+
for (const item of array) {
|
|
407
|
+
const val = fn(item);
|
|
408
|
+
if (val < bestVal) {
|
|
409
|
+
bestVal = val;
|
|
410
|
+
best = item;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return best;
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Creates an array of a given length, filled by a value or generator function.
|
|
418
|
+
*
|
|
419
|
+
* @example
|
|
420
|
+
* nodeComfort.arr.times(3, "x"); // ["x", "x", "x"]
|
|
421
|
+
* nodeComfort.arr.times(3, (i) => i * 2); // [0, 2, 4]
|
|
422
|
+
*
|
|
423
|
+
* @param {number} n - Number of items.
|
|
424
|
+
* @param {any|((index: number) => any)} [value] - Fill value or generator.
|
|
425
|
+
* @returns {any[]} The generated array.
|
|
426
|
+
*/
|
|
427
|
+
times(n, value) {
|
|
428
|
+
const fn = typeof value === "function" ? value : () => value;
|
|
429
|
+
const result = [];
|
|
430
|
+
for (let i = 0; i < n; i++) result.push(fn(i));
|
|
431
|
+
return result;
|
|
432
|
+
},
|
|
433
|
+
};
|