utilitish 0.0.6 → 0.0.7

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