utilitish 0.0.14 → 0.0.16
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/dist/array/array-constructor.js +8 -4
- package/dist/array/array-prototype.d.ts +24 -1
- package/dist/array/array-prototype.js +94 -17
- package/dist/array/array-prototype.spec.js +16 -16
- package/dist/map/map-prototype.js +18 -18
- package/dist/object/object-prototype.js +4 -3
- package/dist/object/object-prototype.spec.js +0 -2
- package/dist/set/set-prototype.d.ts +1 -1
- package/dist/set/set-prototype.js +4 -4
- package/dist/string/string-prototype.d.ts +97 -0
- package/dist/string/string-prototype.js +63 -14
- package/dist/string/string-prototype.spec.js +36 -0
- package/dist/utils/core.utils.d.ts +1 -0
- package/dist/utils/core.utils.js +6 -1
- package/dist/utils/core.utils.spec.d.ts +1 -0
- package/dist/utils/core.utils.spec.js +150 -0
- package/dist/utils/logic.utils.js +4 -0
- package/dist/utils/logic.utils.spec.d.ts +1 -0
- package/dist/utils/logic.utils.spec.js +57 -0
- package/dist/utils/slugify.config.d.ts +10 -2
- package/dist/utils/slugify.config.js +20 -6
- package/dist/utils/slugify.config.spec.d.ts +1 -0
- package/dist/utils/slugify.config.spec.js +144 -0
- package/package.json +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_utils_1 = require("../utils/core.utils");
|
|
4
|
+
const core_utils_2 = require("./../utils/core.utils");
|
|
4
5
|
/**
|
|
5
6
|
* @see Array.zip
|
|
6
7
|
*/
|
|
@@ -19,7 +20,7 @@ const core_utils_1 = require("../utils/core.utils");
|
|
|
19
20
|
start = 0;
|
|
20
21
|
}
|
|
21
22
|
if (step === 0)
|
|
22
|
-
|
|
23
|
+
(0, core_utils_2.utilitishError)('Array.range', 'step must not be 0', undefined, RangeError);
|
|
23
24
|
const result = [];
|
|
24
25
|
const condition = step > 0 ? (i) => i < end : (i) => i > end;
|
|
25
26
|
for (let i = start; condition(i); i += step) {
|
|
@@ -31,9 +32,12 @@ const core_utils_1 = require("../utils/core.utils");
|
|
|
31
32
|
* @see Array.repeat
|
|
32
33
|
*/
|
|
33
34
|
(0, core_utils_1.defineStaticIfNotExists)(Array, 'repeat', function (length, value) {
|
|
34
|
-
if (typeof length !== 'number'
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
if (typeof length !== 'number')
|
|
36
|
+
(0, core_utils_2.utilitishError)('Array.repeat', 'n must be a number', length);
|
|
37
|
+
if (!Number.isInteger(length))
|
|
38
|
+
(0, core_utils_2.utilitishError)('Array.repeat', 'n must be an integer', length);
|
|
39
|
+
if (length < 0)
|
|
40
|
+
(0, core_utils_2.utilitishError)('Array.repeat', 'n must be non-negative', length, RangeError);
|
|
37
41
|
return Array.from({ length }, () => (typeof value === 'function' ? value() : value));
|
|
38
42
|
});
|
|
39
43
|
/**
|
|
@@ -332,7 +332,9 @@ declare global {
|
|
|
332
332
|
* - Symbols are not supported in plain objects
|
|
333
333
|
*/
|
|
334
334
|
toObject<K extends PropertyKey, V>(this: [K, V][]): Record<K, V>;
|
|
335
|
-
toObject<K extends PropertyKey, V>(this: T[]
|
|
335
|
+
toObject<K extends PropertyKey, V>(this: T[]): Record<number, T>;
|
|
336
|
+
toObject<K extends PropertyKey, V>(this: T[], keySelector: Selector<T, K>): Record<K, T>;
|
|
337
|
+
toObject<K extends PropertyKey, V>(this: T[], keySelector: Selector<T, K>, valueSelector: Selector<T, V>): Record<K, V>;
|
|
336
338
|
/**
|
|
337
339
|
* Returns a Set containing the unique elements of the array.
|
|
338
340
|
* Optionally applies a selector function or property key to extract values for the Set.
|
|
@@ -386,5 +388,26 @@ declare global {
|
|
|
386
388
|
* - Empty array returns empty Map
|
|
387
389
|
*/
|
|
388
390
|
countBy<K>(this: T[], selector?: Selector<T, K>): Map<T | K, number>;
|
|
391
|
+
/**
|
|
392
|
+
* Checks if the slugified version of a value matches any slugified string in this array.
|
|
393
|
+
* Useful for case-insensitive and accent-insensitive array search.
|
|
394
|
+
*
|
|
395
|
+
* @this {string[]} The array of strings to search in.
|
|
396
|
+
* @param {string} value - The string to search for.
|
|
397
|
+
* @returns {boolean} `true` if any slugified item in the array equals the slugified `value`, `false` otherwise.
|
|
398
|
+
* @throws {TypeError} If `value` is not a string.
|
|
399
|
+
* @throws {TypeError} If any item in the array is not a string.
|
|
400
|
+
*
|
|
401
|
+
* @example
|
|
402
|
+
* ['Hello World', 'Foo Bar'].slugifyIncludes('hello-world'); // true
|
|
403
|
+
* ['Héllo World', 'Foo Bar'].slugifyIncludes('hello-world'); // true
|
|
404
|
+
* ['Hello World', 'Foo Bar'].slugifyIncludes('baz'); // false
|
|
405
|
+
*
|
|
406
|
+
* @remarks
|
|
407
|
+
* - All strings are slugified before comparison.
|
|
408
|
+
* - Since `slugify` is idempotent, passing an already-slugified value works as expected.
|
|
409
|
+
* - Throws on the first non-string item encountered in the array.
|
|
410
|
+
*/
|
|
411
|
+
slugifyIncludes(value: string): boolean;
|
|
389
412
|
}
|
|
390
413
|
}
|
|
@@ -2,43 +2,67 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_utils_1 = require("../utils/core.utils");
|
|
4
4
|
const logic_utils_1 = require("../utils/logic.utils");
|
|
5
|
+
/**
|
|
6
|
+
* @see Array.prototype.first
|
|
7
|
+
*/
|
|
5
8
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'first', function () {
|
|
6
9
|
return this[0];
|
|
7
10
|
});
|
|
11
|
+
/**
|
|
12
|
+
* @see Array.prototype.last
|
|
13
|
+
*/
|
|
8
14
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'last', function () {
|
|
9
15
|
return this[this.length - 1];
|
|
10
16
|
});
|
|
17
|
+
/**
|
|
18
|
+
* @see Array.prototype.sum
|
|
19
|
+
*/
|
|
11
20
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'sum', function (selector) {
|
|
12
21
|
if (this.length === 0)
|
|
13
22
|
return 0;
|
|
14
23
|
const getValue = (0, core_utils_1.resolveSelector)(selector, (item) => item);
|
|
15
|
-
if (this.every((item) => typeof getValue(item) === 'number')) {
|
|
16
|
-
|
|
24
|
+
if (!this.every((item) => typeof getValue(item) === 'number')) {
|
|
25
|
+
(0, core_utils_1.utilitishError)('Array.prototype.sum', 'requires a selector that returns a number unless array is number[]');
|
|
17
26
|
}
|
|
18
|
-
|
|
27
|
+
return this.reduce((acc, item) => getValue(item) + acc, 0);
|
|
19
28
|
});
|
|
29
|
+
/**
|
|
30
|
+
* @see Array.prototype.unique
|
|
31
|
+
*/
|
|
20
32
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'unique', function () {
|
|
21
33
|
return [...new Set(this)];
|
|
22
34
|
});
|
|
35
|
+
/**
|
|
36
|
+
* @see Array.prototype.chunk
|
|
37
|
+
*/
|
|
23
38
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'chunk', function (size) {
|
|
24
|
-
if (typeof size !== 'number'
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
if (typeof size !== 'number')
|
|
40
|
+
(0, core_utils_1.utilitishError)('Array.prototype.chunk', 'size must be a number', size);
|
|
41
|
+
if (!Number.isInteger(size))
|
|
42
|
+
(0, core_utils_1.utilitishError)('Array.prototype.chunk', 'size must be an integer', size);
|
|
43
|
+
if (size <= 0)
|
|
44
|
+
(0, core_utils_1.utilitishError)('Array.prototype.chunk', 'size must be positive', size, RangeError);
|
|
27
45
|
const result = [];
|
|
28
46
|
for (let i = 0; i < this.length; i += size) {
|
|
29
47
|
result.push(this.slice(i, i + size));
|
|
30
48
|
}
|
|
31
49
|
return result;
|
|
32
50
|
});
|
|
51
|
+
/**
|
|
52
|
+
* @see Array.prototype.average
|
|
53
|
+
*/
|
|
33
54
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'average', function (selector) {
|
|
34
55
|
if (this.length === 0)
|
|
35
56
|
return 0;
|
|
36
57
|
const getValue = (0, core_utils_1.resolveSelector)(selector, (item) => item);
|
|
37
|
-
if (this.every((item) => typeof getValue(item) === 'number')) {
|
|
38
|
-
|
|
58
|
+
if (!this.every((item) => typeof getValue(item) === 'number')) {
|
|
59
|
+
(0, core_utils_1.utilitishError)('Array.prototype.average', 'requires a selector that returns a number unless array is number[]');
|
|
39
60
|
}
|
|
40
|
-
|
|
61
|
+
return this.reduce((acc, item) => getValue(item) + acc, 0) / this.length;
|
|
41
62
|
});
|
|
63
|
+
/**
|
|
64
|
+
* @see Array.prototype.groupBy
|
|
65
|
+
*/
|
|
42
66
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'groupBy', function (selector) {
|
|
43
67
|
const getKey = (0, core_utils_1.resolveSelector)(selector, (item) => item);
|
|
44
68
|
const map = new Map();
|
|
@@ -51,24 +75,34 @@ const logic_utils_1 = require("../utils/logic.utils");
|
|
|
51
75
|
}
|
|
52
76
|
return map;
|
|
53
77
|
});
|
|
78
|
+
/**
|
|
79
|
+
* @see Array.prototype.compact
|
|
80
|
+
*/
|
|
54
81
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'compact', function () {
|
|
55
82
|
return this.filter(Boolean);
|
|
56
83
|
});
|
|
84
|
+
/**
|
|
85
|
+
* @see Array.prototype.enumerate
|
|
86
|
+
*/
|
|
57
87
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'enumerate', function () {
|
|
58
88
|
return this.map((value, index) => [value, index]);
|
|
59
89
|
});
|
|
90
|
+
/**
|
|
91
|
+
* @see Array.prototype.sortAsc
|
|
92
|
+
* @see Array.prototype.sortDesc
|
|
93
|
+
*/
|
|
60
94
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'sortBy', function (direction, selector) {
|
|
61
95
|
if (this.length === 0)
|
|
62
96
|
return this.slice();
|
|
63
97
|
const getValue = (0, core_utils_1.resolveSelector)(selector, (item) => item);
|
|
64
98
|
if (!selector && !this.every((item) => (0, core_utils_1.isNumberOrString)(item))) {
|
|
65
|
-
|
|
99
|
+
(0, core_utils_1.utilitishError)('Array.prototype.sortBy', 'array elements must be number or string if no selector is provided');
|
|
66
100
|
}
|
|
67
101
|
return this.slice().sort((a, b) => {
|
|
68
102
|
const valA = getValue(a);
|
|
69
103
|
const valB = getValue(b);
|
|
70
104
|
if (!(0, core_utils_1.isNumberOrString)(valA) || !(0, core_utils_1.isNumberOrString)(valB)) {
|
|
71
|
-
|
|
105
|
+
(0, core_utils_1.utilitishError)('Array.prototype.sortBy', 'selector must return number or string');
|
|
72
106
|
}
|
|
73
107
|
if (valA > valB)
|
|
74
108
|
return direction === 'asc' ? 1 : -1;
|
|
@@ -77,19 +111,34 @@ const logic_utils_1 = require("../utils/logic.utils");
|
|
|
77
111
|
return 0;
|
|
78
112
|
});
|
|
79
113
|
});
|
|
114
|
+
/**
|
|
115
|
+
* @see Array.prototype.sortAsc
|
|
116
|
+
*/
|
|
80
117
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'sortAsc', function (selector) {
|
|
81
118
|
return (0, logic_utils_1.sortBy)(this, 'asc', selector);
|
|
82
119
|
});
|
|
120
|
+
/**
|
|
121
|
+
* @see Array.prototype.sortDesc
|
|
122
|
+
*/
|
|
83
123
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'sortDesc', function (selector) {
|
|
84
124
|
return (0, logic_utils_1.sortBy)(this, 'desc', selector);
|
|
85
125
|
});
|
|
126
|
+
/**
|
|
127
|
+
* @see Array.prototype.swap
|
|
128
|
+
*/
|
|
86
129
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'swap', function (i, j) {
|
|
87
|
-
if (typeof i !== 'number'
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
130
|
+
if (typeof i !== 'number')
|
|
131
|
+
(0, core_utils_1.utilitishError)('Array.prototype.swap', 'i must be a number', i);
|
|
132
|
+
if (typeof j !== 'number')
|
|
133
|
+
(0, core_utils_1.utilitishError)('Array.prototype.swap', 'j must be a number', j);
|
|
134
|
+
if (!Number.isInteger(i))
|
|
135
|
+
(0, core_utils_1.utilitishError)('Array.prototype.swap', 'i must be an integer', i);
|
|
136
|
+
if (!Number.isInteger(j))
|
|
137
|
+
(0, core_utils_1.utilitishError)('Array.prototype.swap', 'j must be an integer', j);
|
|
138
|
+
if (i < 0 || i >= this.length)
|
|
139
|
+
(0, core_utils_1.utilitishError)('Array.prototype.swap', 'i is out of bounds', i, RangeError);
|
|
140
|
+
if (j < 0 || j >= this.length)
|
|
141
|
+
(0, core_utils_1.utilitishError)('Array.prototype.swap', 'j is out of bounds', j, RangeError);
|
|
93
142
|
if (i !== j) {
|
|
94
143
|
const temp = this[i];
|
|
95
144
|
this[i] = this[j];
|
|
@@ -97,6 +146,9 @@ const logic_utils_1 = require("../utils/logic.utils");
|
|
|
97
146
|
}
|
|
98
147
|
return this;
|
|
99
148
|
});
|
|
149
|
+
/**
|
|
150
|
+
* @see Array.prototype.shuffle
|
|
151
|
+
*/
|
|
100
152
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'shuffle', function () {
|
|
101
153
|
const arr = this.slice();
|
|
102
154
|
for (let i = arr.length - 1; i > 0; i--) {
|
|
@@ -105,6 +157,9 @@ const logic_utils_1 = require("../utils/logic.utils");
|
|
|
105
157
|
}
|
|
106
158
|
return arr;
|
|
107
159
|
});
|
|
160
|
+
/**
|
|
161
|
+
* @see Array.prototype.toMap
|
|
162
|
+
*/
|
|
108
163
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'toMap', function (keySelector, valueSelector) {
|
|
109
164
|
if (!keySelector && this.length && this.every((item) => Array.isArray(item) && item.length === 2)) {
|
|
110
165
|
return new Map(this);
|
|
@@ -120,6 +175,9 @@ const logic_utils_1 = require("../utils/logic.utils");
|
|
|
120
175
|
}
|
|
121
176
|
return map;
|
|
122
177
|
});
|
|
178
|
+
/**
|
|
179
|
+
* @see Array.prototype.toObject
|
|
180
|
+
*/
|
|
123
181
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'toObject', function (keySelector, valueSelector) {
|
|
124
182
|
let entries;
|
|
125
183
|
if (!keySelector && this.length && this.every((item) => Array.isArray(item) && item.length === 2)) {
|
|
@@ -142,10 +200,16 @@ const logic_utils_1 = require("../utils/logic.utils");
|
|
|
142
200
|
}
|
|
143
201
|
return Object.fromEntries(entries);
|
|
144
202
|
});
|
|
203
|
+
/**
|
|
204
|
+
* @see Array.prototype.toSet
|
|
205
|
+
*/
|
|
145
206
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'toSet', function (selector) {
|
|
146
207
|
const getValue = (0, core_utils_1.resolveSelector)(selector, (item) => item);
|
|
147
208
|
return new Set(this.map(getValue));
|
|
148
209
|
});
|
|
210
|
+
/**
|
|
211
|
+
* @see Array.prototype.countBy
|
|
212
|
+
*/
|
|
149
213
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'countBy', function (selector) {
|
|
150
214
|
const getKey = (0, core_utils_1.resolveSelector)(selector, (item) => item);
|
|
151
215
|
const map = new Map();
|
|
@@ -155,3 +219,16 @@ const logic_utils_1 = require("../utils/logic.utils");
|
|
|
155
219
|
}
|
|
156
220
|
return map;
|
|
157
221
|
});
|
|
222
|
+
/**
|
|
223
|
+
* @see Array.prototype.slugifyIncludes
|
|
224
|
+
*/
|
|
225
|
+
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'slugifyIncludes', function (value) {
|
|
226
|
+
if (typeof value !== 'string')
|
|
227
|
+
(0, core_utils_1.utilitishError)('Array.prototype.slugifyIncludes', 'must be a string', value);
|
|
228
|
+
const slugified = value.slugify();
|
|
229
|
+
return this.some((item) => {
|
|
230
|
+
if (typeof item !== 'string')
|
|
231
|
+
(0, core_utils_1.utilitishError)('Array.prototype.slugifyIncludes', 'all array items must be strings', item);
|
|
232
|
+
return item.slugify() === slugified;
|
|
233
|
+
});
|
|
234
|
+
});
|
|
@@ -97,8 +97,8 @@ describe('Array.prototype', () => {
|
|
|
97
97
|
describe('error handling', () => {
|
|
98
98
|
it('should throw TypeError when size is not a positive integer', () => {
|
|
99
99
|
const arr = [1, 2, 3];
|
|
100
|
-
expect(() => arr.chunk(0)).toThrow(
|
|
101
|
-
expect(() => arr.chunk(-1)).toThrow(
|
|
100
|
+
expect(() => arr.chunk(0)).toThrow(RangeError);
|
|
101
|
+
expect(() => arr.chunk(-1)).toThrow(RangeError);
|
|
102
102
|
expect(() => arr.chunk(1.5)).toThrow(TypeError);
|
|
103
103
|
expect(() => arr.chunk('a')).toThrow(TypeError);
|
|
104
104
|
});
|
|
@@ -409,7 +409,7 @@ describe('Array.prototype', () => {
|
|
|
409
409
|
});
|
|
410
410
|
});
|
|
411
411
|
describe('Array.prototype.toObject', () => {
|
|
412
|
-
it('converts array of pairs to object', () => {
|
|
412
|
+
it('should converts array of pairs to object', () => {
|
|
413
413
|
const arr = [
|
|
414
414
|
['a', 1],
|
|
415
415
|
['b', 2],
|
|
@@ -419,7 +419,7 @@ describe('Array.prototype', () => {
|
|
|
419
419
|
expect(obj).toEqual({ a: 1, b: 2, c: 3 });
|
|
420
420
|
expect(typeof obj).toBe('object');
|
|
421
421
|
});
|
|
422
|
-
it('converts array of pairs with numeric keys to object', () => {
|
|
422
|
+
it('should converts array of pairs with numeric keys to object', () => {
|
|
423
423
|
const arr = [
|
|
424
424
|
[1, 'a'],
|
|
425
425
|
[2, 'b'],
|
|
@@ -428,7 +428,7 @@ describe('Array.prototype', () => {
|
|
|
428
428
|
const obj = arr.toObject();
|
|
429
429
|
expect(obj).toEqual({ 1: 'a', 2: 'b', 3: 'c' });
|
|
430
430
|
});
|
|
431
|
-
it('converts array of objects to object using key string', () => {
|
|
431
|
+
it('should converts array of objects to object using key string', () => {
|
|
432
432
|
const arr = [
|
|
433
433
|
{ id: 1, name: 'foo' },
|
|
434
434
|
{ id: 2, name: 'bar' },
|
|
@@ -439,7 +439,7 @@ describe('Array.prototype', () => {
|
|
|
439
439
|
2: { id: 2, name: 'bar' },
|
|
440
440
|
});
|
|
441
441
|
});
|
|
442
|
-
it('converts array of objects to object using key callback', () => {
|
|
442
|
+
it('should converts array of objects to object using key callback', () => {
|
|
443
443
|
const arr = [
|
|
444
444
|
{ id: 1, name: 'foo' },
|
|
445
445
|
{ id: 2, name: 'bar' },
|
|
@@ -450,7 +450,7 @@ describe('Array.prototype', () => {
|
|
|
450
450
|
2: { id: 2, name: 'bar' },
|
|
451
451
|
});
|
|
452
452
|
});
|
|
453
|
-
it('converts array of objects using key callback and value callback', () => {
|
|
453
|
+
it('should converts array of objects using key callback and value callback', () => {
|
|
454
454
|
const arr = [
|
|
455
455
|
{ id: 1, name: 'foo' },
|
|
456
456
|
{ id: 2, name: 'bar' },
|
|
@@ -461,7 +461,7 @@ describe('Array.prototype', () => {
|
|
|
461
461
|
2: 'bar',
|
|
462
462
|
});
|
|
463
463
|
});
|
|
464
|
-
it('converts array without selector (uses index as key)', () => {
|
|
464
|
+
it('should converts array without selector (uses index as key)', () => {
|
|
465
465
|
const arr = ['a', 'b', 'c'];
|
|
466
466
|
const obj = arr.toObject();
|
|
467
467
|
expect(obj).toEqual({
|
|
@@ -470,12 +470,12 @@ describe('Array.prototype', () => {
|
|
|
470
470
|
2: 'c',
|
|
471
471
|
});
|
|
472
472
|
});
|
|
473
|
-
it('converts empty array to empty object', () => {
|
|
473
|
+
it('should converts empty array to empty object', () => {
|
|
474
474
|
const arr = [];
|
|
475
475
|
const obj = arr.toObject();
|
|
476
476
|
expect(obj).toEqual({});
|
|
477
477
|
});
|
|
478
|
-
it('handles objects with string and numeric keys', () => {
|
|
478
|
+
it('should handles objects with string and numeric keys', () => {
|
|
479
479
|
const arr = [
|
|
480
480
|
{ key: 'name', value: 'Alice' },
|
|
481
481
|
{ key: 'age', value: 30 },
|
|
@@ -486,19 +486,19 @@ describe('Array.prototype', () => {
|
|
|
486
486
|
age: 30,
|
|
487
487
|
});
|
|
488
488
|
});
|
|
489
|
-
it('throws error when key selector returns null', () => {
|
|
489
|
+
it('should throws error when key selector returns null', () => {
|
|
490
490
|
const arr = [{ id: 1, name: 'foo' }];
|
|
491
491
|
expect(() => arr.toObject((x) => null, (x) => x.name)).toThrow(TypeError);
|
|
492
492
|
});
|
|
493
|
-
it('throws error when key selector returns undefined', () => {
|
|
493
|
+
it('should throws error when key selector returns undefined', () => {
|
|
494
494
|
const arr = [{ id: 1, name: 'foo' }];
|
|
495
495
|
expect(() => arr.toObject((x) => undefined, (x) => x.name)).toThrow(TypeError);
|
|
496
496
|
});
|
|
497
|
-
it('throws error when key is not a string or number', () => {
|
|
497
|
+
it('should throws error when key is not a string or number', () => {
|
|
498
498
|
const arr = [{ id: { nested: 1 }, name: 'foo' }];
|
|
499
499
|
expect(() => arr.toObject((x) => x.id)).toThrow(TypeError);
|
|
500
500
|
});
|
|
501
|
-
it('overwrites duplicate keys with the last value', () => {
|
|
501
|
+
it('should overwrites duplicate keys with the last value', () => {
|
|
502
502
|
const arr = [
|
|
503
503
|
{ id: 1, value: 'first' },
|
|
504
504
|
{ id: 1, value: 'second' },
|
|
@@ -508,7 +508,7 @@ describe('Array.prototype', () => {
|
|
|
508
508
|
1: 'second',
|
|
509
509
|
});
|
|
510
510
|
});
|
|
511
|
-
it('handles mixed types in array', () => {
|
|
511
|
+
it('should handles mixed types in array', () => {
|
|
512
512
|
const arr = [
|
|
513
513
|
{ id: 'x', value: 10 },
|
|
514
514
|
{ id: 'y', value: 20 },
|
|
@@ -519,7 +519,7 @@ describe('Array.prototype', () => {
|
|
|
519
519
|
y: 20,
|
|
520
520
|
});
|
|
521
521
|
});
|
|
522
|
-
it('converts with string key and different value types', () => {
|
|
522
|
+
it('should converts with string key and different value types', () => {
|
|
523
523
|
const arr = [
|
|
524
524
|
{ id: 1, data: 'text' },
|
|
525
525
|
{ id: 2, data: 42 },
|
|
@@ -13,20 +13,17 @@ const core_utils_1 = require("../utils/core.utils");
|
|
|
13
13
|
case 'object': {
|
|
14
14
|
const obj = {};
|
|
15
15
|
for (const [key, value] of this) {
|
|
16
|
-
if (typeof key
|
|
17
|
-
|
|
18
|
-
obj[String(key)] = value;
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
throw new TypeError(`Map.prototype.toList('object') only supports string, number, or symbol keys. Got: ${typeof key}`);
|
|
16
|
+
if (typeof key !== 'string' && typeof key !== 'number' && typeof key !== 'symbol') {
|
|
17
|
+
(0, core_utils_1.utilitishError)('Map.prototype.toList', `only supports string, number, or symbol keys`, key);
|
|
22
18
|
}
|
|
19
|
+
obj[String(key)] = value;
|
|
23
20
|
}
|
|
24
21
|
return obj;
|
|
25
22
|
}
|
|
26
23
|
case 'entries':
|
|
27
24
|
return Array.from(this.entries());
|
|
28
25
|
default:
|
|
29
|
-
|
|
26
|
+
return (0, core_utils_1.utilitishError)('Map.prototype.toList', `unknown type`, type);
|
|
30
27
|
}
|
|
31
28
|
});
|
|
32
29
|
/**
|
|
@@ -35,12 +32,13 @@ const core_utils_1 = require("../utils/core.utils");
|
|
|
35
32
|
(0, core_utils_1.defineIfNotExists)(Map.prototype, 'toObject', function () {
|
|
36
33
|
const entries = Array.from(this.entries());
|
|
37
34
|
for (const [key] of entries) {
|
|
38
|
-
if (key === null
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
if (key === null)
|
|
36
|
+
(0, core_utils_1.utilitishError)('Map.prototype.toObject', 'key cannot be null');
|
|
37
|
+
if (key === undefined)
|
|
38
|
+
(0, core_utils_1.utilitishError)('Map.prototype.toObject', 'key cannot be undefined');
|
|
41
39
|
const keyType = typeof key;
|
|
42
40
|
if (keyType !== 'string' && keyType !== 'number' && keyType !== 'symbol') {
|
|
43
|
-
|
|
41
|
+
(0, core_utils_1.utilitishError)('Map.prototype.toObject', `keys must be string, number, or symbol`, key);
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
return Object.fromEntries(entries);
|
|
@@ -49,15 +47,17 @@ const core_utils_1 = require("../utils/core.utils");
|
|
|
49
47
|
* @see Map.prototype.ensureArray
|
|
50
48
|
*/
|
|
51
49
|
(0, core_utils_1.defineIfNotExists)(Map.prototype, 'ensureArray', function (key) {
|
|
52
|
-
if (key === null
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
if (key === null)
|
|
51
|
+
(0, core_utils_1.utilitishError)('Map.prototype.ensureArray', 'key cannot be null');
|
|
52
|
+
if (key === undefined)
|
|
53
|
+
(0, core_utils_1.utilitishError)('Map.prototype.ensureArray', 'key cannot be undefined');
|
|
54
|
+
let arr = this.get(key);
|
|
55
|
+
if (arr === undefined) {
|
|
56
|
+
arr = [];
|
|
57
|
+
this.set(key, arr);
|
|
57
58
|
}
|
|
58
|
-
const arr = this.get(key);
|
|
59
59
|
if (!Array.isArray(arr)) {
|
|
60
|
-
|
|
60
|
+
(0, core_utils_1.utilitishError)('Map.prototype.ensureArray', 'value for the key is not an array', arr);
|
|
61
61
|
}
|
|
62
62
|
return arr;
|
|
63
63
|
});
|
|
@@ -11,9 +11,10 @@ const core_utils_1 = require("../utils/core.utils");
|
|
|
11
11
|
* @see Object.prototype.deepMerge
|
|
12
12
|
*/
|
|
13
13
|
(0, core_utils_1.defineIfNotExists)(Object.prototype, 'deepMerge', function (source) {
|
|
14
|
-
if (typeof source !== 'object'
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
if (typeof source !== 'object')
|
|
15
|
+
(0, core_utils_1.utilitishError)('Object.prototype.deepMerge', 'source must be an object', source);
|
|
16
|
+
if (source === null)
|
|
17
|
+
(0, core_utils_1.utilitishError)('Object.prototype.deepMerge', 'source cannot be null', source);
|
|
17
18
|
const isObject = (val) => val !== null && typeof val === 'object' && !Array.isArray(val);
|
|
18
19
|
const merge = (target, source) => {
|
|
19
20
|
for (const key of Object.keys(source)) {
|
|
@@ -24,13 +24,11 @@ describe('Object.prototype', () => {
|
|
|
24
24
|
const source = { b: { d: 3 }, e: 4 };
|
|
25
25
|
const merged = obj.deepMerge(source);
|
|
26
26
|
expect(merged).toEqual({ a: 1, b: { c: 2, d: 3 }, e: 4 });
|
|
27
|
-
console.log(merged);
|
|
28
27
|
});
|
|
29
28
|
it('should overwrite primitive values', () => {
|
|
30
29
|
const obj = { a: 1, b: 2 };
|
|
31
30
|
const merged = obj.deepMerge({ b: 3 });
|
|
32
31
|
expect(merged).toEqual({ a: 1, b: 3 });
|
|
33
|
-
console.log(merged);
|
|
34
32
|
});
|
|
35
33
|
describe('error handling', () => {
|
|
36
34
|
it('should throw if source is not an object', () => {
|
|
@@ -15,7 +15,7 @@ declare global {
|
|
|
15
15
|
* - Returns a new array instance each time; modifying it does not affect the Set
|
|
16
16
|
* - Empty sets return an empty array
|
|
17
17
|
*/
|
|
18
|
-
toList
|
|
18
|
+
toList(): T[];
|
|
19
19
|
/**
|
|
20
20
|
* Returns true if at least one of the given items is present in the Set.
|
|
21
21
|
*
|
|
@@ -13,7 +13,7 @@ const core_utils_1 = require("../utils/core.utils");
|
|
|
13
13
|
*/
|
|
14
14
|
(0, core_utils_1.defineIfNotExists)(Set.prototype, 'hasAny', function (...items) {
|
|
15
15
|
if (!Array.isArray(items))
|
|
16
|
-
|
|
16
|
+
(0, core_utils_1.utilitishError)('Set.prototype.hasAny', 'arguments must be an array', items);
|
|
17
17
|
if (items.length === 0)
|
|
18
18
|
return false;
|
|
19
19
|
return items.some((item) => this.has(item));
|
|
@@ -32,7 +32,7 @@ const core_utils_1 = require("../utils/core.utils");
|
|
|
32
32
|
values = args;
|
|
33
33
|
}
|
|
34
34
|
if (!Array.isArray(values))
|
|
35
|
-
|
|
35
|
+
(0, core_utils_1.utilitishError)('Set.prototype.includes', 'arguments must be an array or a Set', args);
|
|
36
36
|
return values.every((item) => this.has(item));
|
|
37
37
|
});
|
|
38
38
|
/**
|
|
@@ -42,7 +42,7 @@ const core_utils_1 = require("../utils/core.utils");
|
|
|
42
42
|
const result = new Set(this);
|
|
43
43
|
for (const other of others) {
|
|
44
44
|
if (!(other instanceof Set))
|
|
45
|
-
|
|
45
|
+
(0, core_utils_1.utilitishError)('Set.prototype.union', 'arguments must be Sets', other);
|
|
46
46
|
for (const item of other) {
|
|
47
47
|
result.add(item);
|
|
48
48
|
}
|
|
@@ -54,7 +54,7 @@ const core_utils_1 = require("../utils/core.utils");
|
|
|
54
54
|
*/
|
|
55
55
|
(0, core_utils_1.defineIfNotExists)(Set.prototype, 'intersection', function (...others) {
|
|
56
56
|
if (others.some((s) => !(s instanceof Set)))
|
|
57
|
-
|
|
57
|
+
(0, core_utils_1.utilitishError)('Set.prototype.intersection', 'arguments must be Sets', others);
|
|
58
58
|
const result = new Set();
|
|
59
59
|
for (const item of this) {
|
|
60
60
|
if (others.every((set) => set.has(item))) {
|
|
@@ -194,6 +194,103 @@ declare global {
|
|
|
194
194
|
*/
|
|
195
195
|
slugify(): string;
|
|
196
196
|
slugify(config: SlugifyConfig): string;
|
|
197
|
+
/**
|
|
198
|
+
* Compares two strings by slugifying both and checking if they are equal.
|
|
199
|
+
* Useful for case-insensitive and accent-insensitive string comparison.
|
|
200
|
+
*
|
|
201
|
+
* @this {string} The first string to compare.
|
|
202
|
+
* @param {string} other - The second string to compare.
|
|
203
|
+
* @returns {boolean} `true` if both slugified strings are equal, `false` otherwise.
|
|
204
|
+
* @throws {TypeError} If `other` is not a string.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* 'Hello World'.slugifyEquals('hello-world'); // true
|
|
208
|
+
* 'Héllo World'.slugifyEquals('hello-world'); // true
|
|
209
|
+
* 'Hello World'.slugifyEquals('goodbye-world'); // false
|
|
210
|
+
*
|
|
211
|
+
* @remarks
|
|
212
|
+
* - Both strings are slugified before comparison.
|
|
213
|
+
* - Since `slugify` is idempotent, passing an already-slugified string works as expected.
|
|
214
|
+
* - Useful for comparing user-provided strings with stored slugs.
|
|
215
|
+
*/
|
|
216
|
+
slugifyEquals(other: string): boolean;
|
|
217
|
+
/**
|
|
218
|
+
* Checks if the slugified version of this string includes the slugified version of another.
|
|
219
|
+
* Useful for case-insensitive and accent-insensitive substring search.
|
|
220
|
+
*
|
|
221
|
+
* @this {string} The string to search in.
|
|
222
|
+
* @param {string} other - The string to search for.
|
|
223
|
+
* @returns {boolean} `true` if the slugified string contains the slugified `other`, `false` otherwise.
|
|
224
|
+
* @throws {TypeError} If `other` is not a string.
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* 'Hello World'.slugifyIncludes('hello'); // true
|
|
228
|
+
* 'Héllo World'.slugifyIncludes('hello'); // true
|
|
229
|
+
* 'Hello World'.slugifyIncludes('goodbye'); // false
|
|
230
|
+
*
|
|
231
|
+
* @remarks
|
|
232
|
+
* - Both strings are slugified before comparison.
|
|
233
|
+
* - Since `slugify` is idempotent, passing an already-slugified string works as expected.
|
|
234
|
+
*/
|
|
235
|
+
slugifyIncludes(other: string): boolean;
|
|
236
|
+
/**
|
|
237
|
+
* Checks if the slugified version of this string starts with the slugified version of another.
|
|
238
|
+
* Useful for case-insensitive and accent-insensitive prefix matching.
|
|
239
|
+
*
|
|
240
|
+
* @this {string} The string to check.
|
|
241
|
+
* @param {string} other - The prefix to check against.
|
|
242
|
+
* @returns {boolean} `true` if the slugified string starts with the slugified `other`, `false` otherwise.
|
|
243
|
+
* @throws {TypeError} If `other` is not a string.
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* 'Hello World'.slugifyStartsWith('hello'); // true
|
|
247
|
+
* 'Héllo World'.slugifyStartsWith('hello'); // true
|
|
248
|
+
* 'Hello World'.slugifyStartsWith('world'); // false
|
|
249
|
+
*
|
|
250
|
+
* @remarks
|
|
251
|
+
* - Both strings are slugified before comparison.
|
|
252
|
+
* - Since `slugify` is idempotent, passing an already-slugified string works as expected.
|
|
253
|
+
*/
|
|
254
|
+
slugifyStartsWith(other: string): boolean;
|
|
255
|
+
/**
|
|
256
|
+
* Checks if the slugified version of this string ends with the slugified version of another.
|
|
257
|
+
* Useful for case-insensitive and accent-insensitive suffix matching.
|
|
258
|
+
*
|
|
259
|
+
* @this {string} The string to check.
|
|
260
|
+
* @param {string} other - The suffix to check against.
|
|
261
|
+
* @returns {boolean} `true` if the slugified string ends with the slugified `other`, `false` otherwise.
|
|
262
|
+
* @throws {TypeError} If `other` is not a string.
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* 'Hello World'.slugifyEndsWith('world'); // true
|
|
266
|
+
* 'Héllo World'.slugifyEndsWith('world'); // true
|
|
267
|
+
* 'Hello World'.slugifyEndsWith('hello'); // false
|
|
268
|
+
*
|
|
269
|
+
* @remarks
|
|
270
|
+
* - Both strings are slugified before comparison.
|
|
271
|
+
* - Since `slugify` is idempotent, passing an already-slugified string works as expected.
|
|
272
|
+
*/
|
|
273
|
+
slugifyEndsWith(other: string): boolean;
|
|
274
|
+
/**
|
|
275
|
+
* Checks if the slugified version of this string matches any slugified string in a list.
|
|
276
|
+
* Useful for case-insensitive and accent-insensitive list lookup.
|
|
277
|
+
*
|
|
278
|
+
* @this {string} The string to search for.
|
|
279
|
+
* @param {string[]} list - The array of strings to search in.
|
|
280
|
+
* @returns {boolean} `true` if any slugified item in the list equals the slugified string, `false` otherwise.
|
|
281
|
+
* @throws {TypeError} If `list` is not an array.
|
|
282
|
+
* @throws {TypeError} If `list` contains non-string items.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* 'Hello World'.slugifyIn(['hello-world', 'foo']); // true
|
|
286
|
+
* 'Héllo World'.slugifyIn(['hello-world', 'foo']); // true
|
|
287
|
+
* 'Hello World'.slugifyIn(['foo', 'bar']); // false
|
|
288
|
+
*
|
|
289
|
+
* @remarks
|
|
290
|
+
* - All strings are slugified before comparison.
|
|
291
|
+
* - Since `slugify` is idempotent, mixing raw and already-slugified strings in the list works as expected.
|
|
292
|
+
*/
|
|
293
|
+
slugifyIn(list: string[]): boolean;
|
|
197
294
|
/**
|
|
198
295
|
* Replaces a substring between `start` and `end` indices with a given string.
|
|
199
296
|
* Provides precise control over which portion of the string to replace.
|