utilitish 0.0.6 → 0.0.8

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.
@@ -1,132 +1,388 @@
1
- export {};
1
+ import { Selector } from '../utils/core.utils';
2
2
  declare global {
3
3
  interface Array<T> {
4
4
  /**
5
5
  * Returns the first element of the array, or `undefined` if the array is empty.
6
+ *
7
+ * @template T The type of array elements
8
+ * @this {T[]} The array to get the first element from
9
+ * @returns {T | undefined} The first element or undefined if the array is empty
10
+ *
11
+ * @example
12
+ * [1, 2, 3].first(); // 1
13
+ * [].first(); // undefined
6
14
  */
7
15
  first(): T | undefined;
8
16
  /**
9
17
  * Returns the last element of the array, or `undefined` if the array is empty.
18
+ *
19
+ * @template T The type of array elements
20
+ * @this {T[]} The array to get the last element from
21
+ * @returns {T | undefined} The last element or undefined if the array is empty
22
+ *
23
+ * @example
24
+ * [1, 2, 3].last(); // 3
25
+ * [].last(); // undefined
10
26
  */
11
27
  last(): T | undefined;
12
28
  /**
13
29
  * Calculates the sum of the array values.
14
- * - If the array is of type `number[]`, no callback is required.
15
- * - Otherwise, a callback must be provided to extract a numeric value.
30
+ * Supports bare number arrays or objects with a selector to extract numeric values.
31
+ *
32
+ * @template T The type of array elements
33
+ * @this {number[]|T[]} The array to sum
34
+ * @param {Selector<T, number>} [selector] - Optional function or property key to extract numbers
35
+ * @returns {number} The sum of all values (0 for empty arrays)
36
+ * @throws {TypeError} If array is not of type number[] and no selector is provided
16
37
  *
17
- * @param callback - Optional function that returns a `number` from an item.
18
- * @throws {Error} If no callback is provided for a non-number array.
19
- * @returns The sum of the values.
38
+ * @example
39
+ * [1, 2, 3].sum(); // 6
40
+ * [].sum(); // 0
41
+ * [{ x: 1 }, { x: 2 }].sum(x => x.x); // 3
42
+ * [{ x: 1 }, { x: 2 }].sum('x'); // 3
43
+ *
44
+ * @remarks
45
+ * - For number arrays, no selector is required
46
+ * - For object arrays, use property key string or callback function
47
+ * - Returns 0 for empty arrays regardless of type
20
48
  */
21
49
  sum(this: number[]): number;
22
- sum<K extends keyof T>(this: T[], key: K): number;
23
- sum(this: T[], callback: (item: T) => number): number;
50
+ sum(this: T[], selector?: Selector<T, number>): number;
24
51
  /**
25
- * Returns a new array with only unique elements (based on strict equality).
52
+ * Returns a new array with only unique elements based on strict equality (===).
53
+ * Preserves order of first occurrence.
54
+ *
55
+ * @template T The type of array elements
56
+ * @this {T[]} The array to filter
57
+ * @returns {T[]} A new array with duplicate values removed
58
+ *
59
+ * @example
60
+ * [1, 1, 2, 2, 3].unique(); // [1, 2, 3]
61
+ * [{id: 1}, {id: 1}, {id: 2}].unique(); // [{id: 1}, {id: 1}, {id: 2}] (objects compared by reference)
62
+ *
63
+ * @remarks
64
+ * - Uses Set internally for efficiency
65
+ * - Only removes duplicates based on strict equality
66
+ * - For object arrays, same reference is considered equal
26
67
  */
27
68
  unique(): T[];
28
69
  /**
29
- * Splits the array into chunks of the given size.
30
- * @param size - Maximum size of each chunk.
70
+ * Splits the array into chunks (sub-arrays) of a specified maximum size.
71
+ *
72
+ * @template T The type of array elements
73
+ * @this {T[]} The array to chunk
74
+ * @param {number} size - Maximum size of each chunk (must be positive integer)
75
+ * @returns {T[][]} A new array of chunks, where each chunk has at most `size` elements
76
+ * @throws {TypeError} If size is not a positive integer
77
+ *
78
+ * @example
79
+ * [1, 2, 3, 4].chunk(2); // [[1, 2], [3, 4]]
80
+ * [1, 2, 3].chunk(2); // [[1, 2], [3]]
81
+ * [1, 2, 3, 4, 5].chunk(2); // [[1, 2], [3, 4], [5]]
82
+ *
83
+ * @remarks
84
+ * - Last chunk may have fewer elements if array length is not divisible by size
85
+ * - Empty array returns an empty array
31
86
  */
32
87
  chunk(size: number): T[][];
33
88
  /**
34
- * Calculates the average of the array values.
35
- * - If the array is of type `number[]`, no callback is required.
36
- * - Otherwise, a callback must be provided to extract a numeric value.
89
+ * Calculates the average (mean) of the array values.
90
+ * Supports bare number arrays or objects with a selector to extract numeric values.
91
+ *
92
+ * @template T The type of array elements
93
+ * @this {number[]|T[]} The array to average
94
+ * @param {Selector<T, number>} [selector] - Optional function or property key to extract numbers
95
+ * @returns {number} The average of all values (0 for empty arrays)
96
+ * @throws {TypeError} If array is not of type number[] and no selector is provided
37
97
  *
38
- * @param callback - Optional function that returns a `number` from an item.
39
- * @throws {Error} If no callback is provided for a non-number array.
40
- * @returns The average of the values.
98
+ * @example
99
+ * [2, 4, 6].average(); // 4
100
+ * [].average(); // 0
101
+ * [{x: 2}, {x: 4}].average(x => x.x); // 3
102
+ * [{x: 2}, {x: 4}].average('x'); // 3
103
+ *
104
+ * @remarks
105
+ * - For number arrays, no selector is required
106
+ * - Returns 0 for empty arrays (prevents division by zero)
107
+ * - For object arrays, use property key string or callback function
41
108
  */
42
109
  average(this: number[]): number;
43
- average<K extends keyof T>(this: T[], key: K): number;
44
- average(this: T[], callback: (item: T) => number): number;
110
+ average(this: T[], selector: Selector<T, number>): number;
45
111
  /**
46
- * Groups the array elements based on a key returned by the selector.
47
- * The selector can be a function or a string key.
48
- * @param selector Function or string key to group by.
49
- * @returns A Map where keys are group values and values are arrays of grouped items.
112
+ * Groups array elements into a Map based on a key returned by the selector.
113
+ * Elements with the same key are grouped together in an array.
114
+ *
115
+ * @template T The type of array elements
116
+ * @template K The type of grouping key (extracted from selector)
117
+ * @this {T[]} The array to group
118
+ * @param {Selector<T, K>} selector - Function or property key to extract the grouping key
119
+ * @returns {Map<K, T[]>} A Map where keys map to arrays of grouped items
120
+ *
121
+ * @example
122
+ * const arr = [{type: 'a', v: 1}, {type: 'b', v: 2}, {type: 'a', v: 3}];
123
+ * arr.groupBy('type');
124
+ * // Map { 'a' => [{type: 'a', v: 1}, {type: 'a', v: 3}], 'b' => [{type: 'b', v: 2}] }
125
+ *
126
+ * arr.groupBy(x => x.v % 2);
127
+ * // Map { 1 => [{type: 'a', v: 1}, {type: 'a', v: 3}], 0 => [{type: 'b', v: 2}] }
128
+ *
129
+ * @remarks
130
+ * - Order of groups in Map matches insertion order (first occurrence of key)
131
+ * - Empty array returns an empty Map
50
132
  */
51
- groupBy<K extends keyof T>(this: T[], key: K): Map<T[K], T[]>;
52
- groupBy<K>(this: T[], selector: (item: T) => K): Map<K, T[]>;
133
+ groupBy<K>(this: T[], selector: Selector<T, K>): Map<K, T[]>;
53
134
  /**
54
- * Removes all falsy values (`false`, `null`, `0`, `""`, `undefined`, and `NaN`) from the array.
55
- * @returns A new array with all falsy values removed.
135
+ * Removes all falsy values from the array.
136
+ * Removes: `false`, `null`, `0`, `""` (empty string), `undefined`, `NaN`.
137
+ *
138
+ * @template T The type of array elements
139
+ * @this {T[]} The array to compact
140
+ * @returns {T[]} A new array with all falsy values removed
141
+ *
142
+ * @example
143
+ * [0, 1, false, 2, '', 3, null, undefined, NaN].compact();
144
+ * // [1, 2, 3]
145
+ *
146
+ * @remarks
147
+ * - Uses JavaScript's falsy value definition
148
+ * - Returns a new array (original is not modified)
56
149
  */
57
150
  compact(): T[];
58
151
  /**
59
- * Enumerates the array into tuples [value, index].
152
+ * Enumerates the array into tuples of [value, index].
60
153
  * Similar to Python's enumerate but returns value first.
61
- * @returns An array of [value, index] pairs.
154
+ *
155
+ * @template T The type of array elements
156
+ * @this {T[]} The array to enumerate
157
+ * @returns {[T, number][]} An array of [value, index] tuples
158
+ *
159
+ * @example
160
+ * ['a', 'b', 'c'].enumerate();
161
+ * // [['a', 0], ['b', 1], ['c', 2]]
162
+ *
163
+ * @remarks
164
+ * - Value comes first (before index), unlike JavaScript's map callback
165
+ * - Index is always the enumeration index (0-based)
62
166
  */
63
167
  enumerate(): [T, number][];
64
168
  /**
65
- * Returns a sorted copy of the array in ascending order.
66
- * - If no callback is provided, all elements must be of type `number` or `string`.
67
- * - If a callback is provided, it must return a `number` or `string` for each element.
68
- * @param callback Optional function to extract the value to sort by.
69
- * @throws {TypeError} If elements are not sortable or the callback returns an invalid type.
70
- * @returns A new array sorted in ascending order.
71
- */
72
- sortAsc(this: number[] | string[]): T[];
73
- sortAsc<K extends keyof T>(this: T[], key: K): T[];
74
- sortAsc(this: T[], callback: (item: T) => number | string): T[];
75
- /**
76
- * Returns a sorted copy of the array in descending order.
77
- * - If no callback is provided, all elements must be of type `number` or `string`.
78
- * - If a callback is provided, it must return a `number` or `string` for each element.
79
- * @param callback Optional function to extract the value to sort by.
80
- * @throws {TypeError} If elements are not sortable or the callback returns an invalid type.
81
- * @returns A new array sorted in descending order.
82
- */
83
- sortDesc(this: number[] | string[]): T[];
84
- sortDesc<K extends keyof T>(this: T[], key: K): T[];
85
- sortDesc(this: T[], callback: (item: T) => number | string): T[];
86
- /**
87
- * Swaps the values at two indices in the array.
88
- * @param i First index
89
- * @param j Second index
90
- * @returns The array itself after swapping
169
+ * Returns a new sorted copy of the array in ascending order.
170
+ * Creates a new array without modifying the original.
171
+ *
172
+ * @template T The type of array elements
173
+ * @this {number[]|string[]|T[]} The array to sort
174
+ * @param {Selector<T, number|string>} [selector] - Optional function or property key to extract sortable value
175
+ * @returns {T[]} A new array sorted in ascending order
176
+ * @throws {TypeError} If elements are not sortable or selector returns invalid type
177
+ *
178
+ * @example
179
+ * [3, 1, 2].sortAsc(); // [1, 2, 3]
180
+ * ['c', 'a', 'b'].sortAsc(); // ['a', 'b', 'c']
181
+ * [{v: 2}, {v: 1}].sortAsc(x => x.v); // [{v: 1}, {v: 2}]
182
+ * [{v: 2}, {v: 1}].sortAsc('v'); // [{v: 1}, {v: 2}]
183
+ *
184
+ * @remarks
185
+ * - For primitive arrays, no selector needed (must be number or string)
186
+ * - Selectors must return number or string for comparison
187
+ * - Returns new array; does not mutate original
188
+ * - Empty arrays return empty array
189
+ */
190
+ sortAsc(this: T[], selector?: Selector<T, number | string>): T[];
191
+ /**
192
+ * Returns a new sorted copy of the array in descending order.
193
+ * Creates a new array without modifying the original.
194
+ *
195
+ * @template T The type of array elements
196
+ * @this {number[]|string[]|T[]} The array to sort
197
+ * @param {Selector<T, number|string>} [selector] - Optional function or property key to extract sortable value
198
+ * @returns {T[]} A new array sorted in descending order
199
+ * @throws {TypeError} If elements are not sortable or selector returns invalid type
200
+ *
201
+ * @example
202
+ * [1, 3, 2].sortDesc(); // [3, 2, 1]
203
+ * ['a', 'c', 'b'].sortDesc(); // ['c', 'b', 'a']
204
+ * [{v: 1}, {v: 2}].sortDesc(x => x.v); // [{v: 2}, {v: 1}]
205
+ * [{v: 1}, {v: 2}].sortDesc('v'); // [{v: 2}, {v: 1}]
206
+ *
207
+ * @remarks
208
+ * - For primitive arrays, no selector needed (must be number or string)
209
+ * - Selectors must return number or string for comparison
210
+ * - Returns new array; does not mutate original
211
+ * - Empty arrays return empty array
212
+ */
213
+ sortDesc(this: T[], selector?: Selector<T, number | string>): T[];
214
+ /**
215
+ * Swaps the elements at two indices within the array.
216
+ * Modifies the array in place and returns the array itself (for chaining).
217
+ *
218
+ * @template T The type of array elements
219
+ * @this {T[]} The array to modify
220
+ * @param {number} i - First index to swap
221
+ * @param {number} j - Second index to swap
222
+ * @returns {T[]} The modified array (same reference as this)
223
+ * @throws {TypeError} If indices are not integers
224
+ * @throws {RangeError} If any index is out of bounds (negative or >= length)
225
+ *
226
+ * @example
227
+ * const arr = [1, 2, 3];
228
+ * arr.swap(0, 2); // arr is now [3, 2, 1]
229
+ * arr === arr.swap(0, 2); // true (returns same array)
230
+ *
231
+ * @remarks
232
+ * - Does nothing if i === j
233
+ * - Modifies original array (not immutable)
234
+ * - Validates both indices are valid integers
91
235
  */
92
236
  swap(i: number, j: number): this;
93
237
  /**
94
- * Returns a new array with the elements shuffled in random order.
95
- * Uses the Fisher-Yates shuffle algorithm.
96
- * @returns A new shuffled array.
238
+ * Returns a new array with elements shuffled randomly.
239
+ * Uses Fisher-Yates algorithm for uniform distribution.
240
+ * Does not modify the original array.
241
+ *
242
+ * @template T The type of array elements
243
+ * @this {T[]} The array to shuffle
244
+ * @returns {T[]} A new shuffled array
245
+ *
246
+ * @example
247
+ * const arr = [1, 2, 3, 4, 5];
248
+ * const shuffled = arr.shuffle();
249
+ * // shuffled might be [3, 1, 5, 2, 4] (order is random)
250
+ * arr; // [1, 2, 3, 4, 5] (unchanged)
251
+ *
252
+ * @remarks
253
+ * - Returns new array instance (original unchanged)
254
+ * - Uses Math.random() so results vary
255
+ * - Empty arrays return empty array
256
+ * - Fisher-Yates algorithm ensures unbiased distribution
97
257
  */
98
258
  shuffle(): T[];
99
259
  /**
100
- * Converts an array to a Map.
101
- * - If the array is of pairs [K, V], returns Map<K, V>.
102
- * - If a key is provided, returns Map<T[K], T>.
103
- * - If keySelector and valueSelector are provided, returns Map<K, V>.
104
- * @param keyOrKeySelector Key name or key selector function.
105
- * @param valueSelector Value selector function.
260
+ * Converts an array to a Map using keys and optionally values extracted from elements.
261
+ * Supports multiple input formats: pairs array, property key, or selector functions.
262
+ *
263
+ * @template T The type of array elements
264
+ * @template K The type of Map keys
265
+ * @template V The type of Map values
266
+ * @this {[K, V][]|T[]} The array to convert
267
+ * @param {K | (item: T) => K} [keySelector] - Property key or function to extract keys
268
+ * @param {(item: T) => V} [valueSelector] - Optional function to extract values
269
+ * @returns {Map<K, V>} A Map with extracted key-value pairs
270
+ *
271
+ * @example
272
+ * // From pairs
273
+ * [['a', 1], ['b', 2]].toMap(); // Map { 'a' => 1, 'b' => 2 }
274
+ *
275
+ * // Using property key
276
+ * [{id: 1, name: 'foo'}, {id: 2, name: 'bar'}].toMap('id');
277
+ * // Map { 1 => {id: 1, name: 'foo'}, 2 => {id: 2, name: 'bar'} }
278
+ *
279
+ * // Using selectors
280
+ * [{id: 1, name: 'foo'}].toMap(x => x.id, x => x.name);
281
+ * // Map { 1 => 'foo' }
282
+ *
283
+ * // Default (uses index)
284
+ * ['a', 'b'].toMap();
285
+ * // Map { 0 => 'a', 1 => 'b' }
286
+ *
287
+ * @remarks
288
+ * - For pairs array, no parameters needed
289
+ * - For objects, provide key as string or selector function
290
+ * - Value defaults to the entire element if not specified
291
+ * - Index is used as key if no keySelector provided
106
292
  */
107
293
  toMap<K, V>(this: [K, V][]): Map<K, V>;
108
- toMap<K extends keyof T>(this: T[], key: K): Map<T[K], T>;
109
- toMap<K, V>(): Map<number, T>;
110
- toMap<K, V>(this: T[], keyCallback: (item: T) => K): Map<K, T>;
111
- toMap<K, V>(this: T[], keyCallback: (item: T) => K, valueCallback: (item: T) => V): Map<K, V>;
294
+ toMap<K, V>(this: T[], keySelector?: Selector<T, K>, valueSelector?: Selector<T, V>): Map<K | number, V | T>;
295
+ /**
296
+ * Converts an array to a plain JavaScript object using keys and optionally values extracted from elements.
297
+ * Leverages Map.prototype.toObject() internally for consistency and validation.
298
+ *
299
+ * @template T The type of array elements
300
+ * @template K Type of object keys (must be PropertyKey: string | number)
301
+ * @template V Type of object values
302
+ * @this {[K, V][]|T[]} The array to convert
303
+ * @param {K | keyof T | (item: T) => K} [keySelector] - Property key or function to extract keys
304
+ * @param {(item: T) => V} [valueSelector] - Optional function to extract values
305
+ * @returns {Record<K, V>} A plain object with array elements as properties
306
+ * @throws {TypeError} If any key is null, undefined, or not a valid PropertyKey (string/number)
307
+ *
308
+ * @example
309
+ * // From pairs
310
+ * [['a', 1], ['b', 2]].toObject(); // { a: 1, b: 2 }
311
+ *
312
+ * // Using property key
313
+ * [{id: 1, name: 'foo'}, {id: 2, name: 'bar'}].toObject('id');
314
+ * // { 1: {id: 1, name: 'foo'}, 2: {id: 2, name: 'bar'} }
315
+ *
316
+ * // Using selectors
317
+ * [{id: 1, name: 'foo'}].toObject(x => x.id, x => x.name);
318
+ * // { 1: 'foo' }
319
+ *
320
+ * // Default (uses index)
321
+ * ['a', 'b'].toObject();
322
+ * // { 0: 'a', 1: 'b' }
323
+ *
324
+ * @remarks
325
+ * - For pairs array, no parameters needed
326
+ * - For objects, provide key as string or selector function
327
+ * - Value defaults to the entire element if not specified
328
+ * - Index is used as key if no keySelector provided
329
+ * - Validates keys are valid PropertyKeys (not null/undefined)
330
+ * - Symbols are not supported in plain objects
331
+ */
332
+ toObject<K extends PropertyKey, V>(this: [K, V][]): Record<K, V>;
333
+ toObject<K extends PropertyKey, V>(this: T[], keySelector?: Selector<T, K>, valueSelector?: Selector<T, V>): Record<K | number, V | T>;
112
334
  /**
113
335
  * Returns a Set containing the unique elements of the array.
114
- * If a selector is provided, it can be a function or a string key.
115
- * - If a function, it is called for each element.
116
- * - If a string, it is used as a property key of each element.
117
- * @param selector Optional function or string key to select the value to store in the Set.
118
- * @returns A Set of unique elements or selected values.
119
- */
120
- toSet(): Set<T>;
121
- toSet<K extends keyof T>(this: T[], key: K): Set<T[K]>;
122
- toSet<K>(this: T[], selector: (item: T) => K): Set<K>;
123
- /**
124
- * Returns a Map where the keys are the result of the selector (function or string key) and the values are the counts of each key.
125
- * @param selector Function or string key to select the key for counting.
126
- * @returns A Map with the count of each key.
127
- */
128
- countBy(): Map<T, number>;
129
- countBy<K extends keyof T>(this: T[], key: K): Map<T[K], number>;
130
- countBy<K>(this: T[], selector: (item: T) => K): Map<K, number>;
336
+ * Optionally applies a selector function or property key to extract values for the Set.
337
+ *
338
+ * @template T The type of array elements
339
+ * @template K The type of selected values for the Set
340
+ * @this {T[]} The array to convert
341
+ * @param {Selector<T, K>} [selector] - Optional property key or function to select values
342
+ * @returns {Set<T | K>} A Set of unique elements or selected values
343
+ *
344
+ * @example
345
+ * [1, 2, 2, 3].toSet(); // Set { 1, 2, 3 }
346
+ *
347
+ * [{id: 1}, {id: 2}, {id: 1}].toSet(x => x.id);
348
+ * // Set { 1, 2 }
349
+ *
350
+ * [{id: 1}, {id: 2}].toSet('id');
351
+ * // Set { 1, 2 }
352
+ *
353
+ * @remarks
354
+ * - Without selector, stores the entire element in the Set
355
+ * - With selector, stores the extracted value instead
356
+ * - Uses Set's built-in uniqueness (based on === equality)
357
+ * - Empty array returns empty Set
358
+ */
359
+ toSet<K>(this: T[], selector?: Selector<T, K>): Set<T | K>;
360
+ /**
361
+ * Groups array elements by a key and counts the occurrences of each key.
362
+ * Returns a Map where keys map to their occurrence counts.
363
+ *
364
+ * @template T The type of array elements
365
+ * @template K The type of grouping key
366
+ * @this {T[]} The array to count
367
+ * @param {Selector<T, K>} [selector] - Optional property key or function to extract grouping key
368
+ * @returns {Map<T | K, number>} A Map where keys map to their occurrence counts
369
+ *
370
+ * @example
371
+ * ['a', 'b', 'a', 'c', 'b', 'a'].countBy();
372
+ * // Map { 'a' => 3, 'b' => 2, 'c' => 1 }
373
+ *
374
+ * [{type: 'x'}, {type: 'y'}, {type: 'x'}].countBy(x => x.type);
375
+ * // Map { 'x' => 2, 'y' => 1 }
376
+ *
377
+ * [{type: 'x'}, {type: 'y'}].countBy('type');
378
+ * // Map { 'x' => 1, 'y' => 1 }
379
+ *
380
+ * @remarks
381
+ * - Without selector, counts entire elements
382
+ * - With selector, counts extracted keys
383
+ * - Uses === equality for counting
384
+ * - Empty array returns empty Map
385
+ */
386
+ countBy<K>(this: T[], selector?: Selector<T, K>): Map<T | K, number>;
131
387
  }
132
388
  }