@@ -10,15 +10,15 @@ import { getValueByPath } from "./obj.utils";
1010 * @throws {TypeError } If array is not an array.
1111 * @throws {Error } If chunk size is not a positive integer.
1212 */
13- export const chunk = < T > ( array : T [ ] , size : number ) : T [ ] [ ] => {
13+ export function chunk < T > ( array : T [ ] , size : number ) : T [ ] [ ] {
1414 if ( ! Array . isArray ( array ) ) throw new TypeError ( "Expected an array" ) ;
1515 if ( ! array . length ) return [ ] ;
1616 if ( ! Number . isInteger ( size ) || size <= 0 )
1717 throw new Error ( "Chunk size must be a positive integer" ) ;
1818 return Array . from ( { length : Math . ceil ( array . length / size ) } , ( _ , i ) =>
1919 array . slice ( i * size , i * size + size ) ,
2020 ) ;
21- } ;
21+ }
2222
2323/**
2424 * Removes duplicate values from an array.
@@ -229,3 +229,157 @@ export function mergeSort<T>(
229229 } ;
230230 return sort ( array ) ;
231231}
232+
233+ /**
234+ * Combines multiple arrays into a single array of grouped elements.
235+ * Output array length equals the length of the shortest input array.
236+ *
237+ * @example zip([1, 2], ['a', 'b']) => [[1, 'a'], [2, 'b']]
238+ * @param {...Array<T>[] } arrays - Two or more arrays to zip together.
239+ * @returns {Array<T[]> } Array of grouped elements.
240+ */
241+ export function zip < T > ( ...arrays : T [ ] [ ] ) : T [ ] [ ] {
242+ if ( arrays . length === 0 ) return [ ] ;
243+ if ( arrays . some ( ( arr ) => ! Array . isArray ( arr ) ) ) {
244+ throw new TypeError ( "All arguments must be arrays" ) ;
245+ }
246+
247+ const minLength = Math . min ( ...arrays . map ( ( arr ) => arr . length ) ) ;
248+ const result : T [ ] [ ] = [ ] ;
249+
250+ for ( let i = 0 ; i < minLength ; i ++ ) {
251+ result . push ( arrays . map ( ( arr ) => arr [ i ] ) ) ;
252+ }
253+
254+ return result ;
255+ }
256+
257+ /**
258+ * Splits an array into two arrays based on a predicate function.
259+ *
260+ * @template T The type of array elements.
261+ * @param {T[] } array - The input array.
262+ * @param {(item: T, index: number, array: T[]) => boolean } predicate - Function to test each element.
263+ * @returns {[T[], T[]] } A tuple of two arrays: [matched, unmatched].
264+ */
265+ export function partition < T > (
266+ array : T [ ] ,
267+ predicate : ( item : T , index : number , array : T [ ] ) => boolean ,
268+ ) : [ T [ ] , T [ ] ] {
269+ if ( ! Array . isArray ( array ) ) return [ [ ] , [ ] ] ;
270+
271+ return array . reduce (
272+ ( [ pass , fail ] , item , index ) => {
273+ return predicate ( item , index , array )
274+ ? [ [ ...pass , item ] , fail ]
275+ : [ pass , [ ...fail , item ] ] ;
276+ } ,
277+ [ [ ] as T [ ] , [ ] as T [ ] ] ,
278+ ) ;
279+ }
280+
281+ /**
282+ * Generates an array of numbers within a specified range.
283+ *
284+ * @param {number } start - Start of range (inclusive).
285+ * @param {number } end - End of range (exclusive).
286+ * @param {number } [step=1] - Step between numbers.
287+ * @returns {number[] } Array of numbers in range.
288+ */
289+ export function range ( start : number , end : number , step : number = 1 ) : number [ ] {
290+ if (
291+ ! Number . isFinite ( start ) ||
292+ ! Number . isFinite ( end ) ||
293+ ! Number . isFinite ( step )
294+ ) {
295+ throw new TypeError ( "Arguments must be finite numbers" ) ;
296+ }
297+ if ( step === 0 ) throw new Error ( "Step cannot be zero" ) ;
298+
299+ const isAscending = step > 0 ;
300+ if ( ( isAscending && start >= end ) || ( ! isAscending && start <= end ) ) {
301+ return [ ] ;
302+ }
303+
304+ const length = Math . max ( Math . ceil ( ( end - start ) / step ) , 0 ) ;
305+ const result = new Array ( length ) ;
306+
307+ for ( let i = 0 , value = start ; i < length ; i ++ , value += step ) {
308+ result [ i ] = value ;
309+ }
310+
311+ return result ;
312+ }
313+
314+ /**
315+ * Returns the first n elements of an array.
316+ *
317+ * @template T The type of array elements.
318+ * @param {T[] } array - The input array.
319+ * @param {number } [n=1] - Number of elements to take.
320+ * @returns {T[] } New array with first n elements.
321+ */
322+ export function take < T > ( array : T [ ] , n : number = 1 ) : T [ ] {
323+ if ( ! Array . isArray ( array ) || n <= 0 ) return [ ] ;
324+ return array . slice ( 0 , n ) ;
325+ }
326+
327+ /**
328+ * Takes elements from the array while predicate returns true.
329+ *
330+ * @template T The type of array elements.
331+ * @param {T[] } array - The input array.
332+ * @param {(item: T, index: number) => boolean } predicate - Function to test each element.
333+ * @returns {T[] } New array with taken elements.
334+ */
335+ export function takeWhile < T > (
336+ array : T [ ] ,
337+ predicate : ( item : T , index : number ) => boolean ,
338+ ) : T [ ] {
339+ if ( ! Array . isArray ( array ) ) return [ ] ;
340+
341+ const result : T [ ] = [ ] ;
342+ for ( let i = 0 ; i < array . length ; i ++ ) {
343+ if ( ! predicate ( array [ i ] , i ) ) break ;
344+ result . push ( array [ i ] ) ;
345+ }
346+
347+ return result ;
348+ }
349+
350+ /**
351+ * Removes all falsy values from an array.
352+ * False, null, 0, "", undefined, and NaN are falsy.
353+ *
354+ * @template T The type of array elements.
355+ * @param {T[] } array - The input array.
356+ * @returns {NonNullable<T>[] } New array with falsy values removed.
357+ */
358+ export function compact < T > ( array : T [ ] ) : NonNullable < T > [ ] {
359+ if ( ! Array . isArray ( array ) ) return [ ] ;
360+ return array . filter ( Boolean ) as NonNullable < T > [ ] ;
361+ }
362+
363+ /**
364+ * Counts array elements by a key function.
365+ *
366+ * @template T The type of array elements.
367+ * @param {T[] } array - The input array.
368+ * @param {(item: T) => string | number | symbol } keyFn - Function to generate count key.
369+ * @returns {Record<string, number> } Object with counts by key.
370+ */
371+ export function countBy < T > (
372+ array : T [ ] ,
373+ keyFn : ( item : T ) => string | number | symbol ,
374+ ) : Record < string , number > {
375+ if ( ! Array . isArray ( array ) ) return { } ;
376+
377+ return array . reduce (
378+ ( acc , item ) => {
379+ const key = String ( keyFn ( item ) ) ;
380+ acc [ key ] = ( acc [ key ] || 0 ) + 1 ;
381+ return acc ;
382+ } ,
383+ { } as Record < string , number > ,
384+ ) ;
385+ }
0 commit comments