1- import { getValueByPath } from '@catbee/utils/obj ' ;
1+ import { getValueByPath } from '@catbee/utils/object ' ;
22import { randomBytes } from 'node:crypto' ;
33
44/**
@@ -85,6 +85,8 @@ export function random<T>(array: readonly T[]): T | undefined {
8585 return array [ secureIndex ( array . length ) ] ;
8686}
8787
88+ type StrNumSym = string | number | symbol ;
89+
8890/* eslint-disable no-redeclare */
8991/**
9092 * Groups items in an array by a nested key or key function.
@@ -96,20 +98,17 @@ export function random<T>(array: readonly T[]): T | undefined {
9698 * @returns {Record<string, readonly T[]> }
9799 * @overload
98100 * @param {T[] } array - The array to group.
99- * @param {(item: T) => string | number | symbol } keyFn - Function to generate group key from item.
101+ * @param {(item: T) => StrNumSym } keyFn - Function to generate group key from item.
100102 * @returns {Record<K, readonly T[]> }
101103 * @param {T[] } array - The array to group.
102- * @param {keyof T | ((item: T) => string | number | symbol ) } keyOrFn - Nested property key or key selector.
103- * @returns {Record<string | number | symbol , readonly T[]> } Grouped result object.
104+ * @param {keyof T | ((item: T) => StrNumSym ) } keyOrFn - Nested property key or key selector.
105+ * @returns {Record<StrNumSym , readonly T[]> } Grouped result object.
104106 */
105107export function groupBy < T > ( array : T [ ] , key : keyof T ) : Record < string , readonly T [ ] > ;
106- export function groupBy < T , K extends string | number | symbol > (
107- array : T [ ] ,
108- keyFn : ( item : T ) => K
109- ) : Record < K , readonly T [ ] > ;
108+ export function groupBy < T , K extends StrNumSym > ( array : T [ ] , keyFn : ( item : T ) => K ) : Record < K , readonly T [ ] > ;
110109export function groupBy < T > (
111110 array : readonly T [ ] ,
112- keyOrFn : keyof T | ( ( item : T ) => string | number | symbol )
111+ keyOrFn : keyof T | ( ( item : T ) => StrNumSym )
113112) : Record < string , readonly T [ ] > {
114113 if ( ! Array . isArray ( array ) || array . length === 0 ) return { } ;
115114
@@ -119,7 +118,7 @@ export function groupBy<T>(
119118 const result : Record < string , T [ ] > = { } ;
120119 for ( const item of array ) {
121120 const key = String ( keyFn ( item ) ) ;
122- if ( Object . hasOwn ?. ( result , key ) ?? Object . prototype . hasOwnProperty . call ( result , key ) ) {
121+ if ( Object . hasOwn ( result , key ) ) {
123122 result [ key ] . push ( item ) ;
124123 } else {
125124 result [ key ] = [ item ] ;
@@ -403,10 +402,10 @@ export function compact<T>(array: readonly T[]): NonNullable<T>[] {
403402 *
404403 * @template T The type of array elements.
405404 * @param {T[] } array - The input array.
406- * @param {(item: T) => string | number | symbol } keyFn - Function to generate count key.
405+ * @param {(item: T) => StrNumSym } keyFn - Function to generate count key.
407406 * @returns {Record<string, number> } Object with counts by key.
408407 */
409- export function countBy < T > ( array : readonly T [ ] , keyFn : ( item : T ) => string | number | symbol ) : Record < string , number > {
408+ export function countBy < T > ( array : readonly T [ ] , keyFn : ( item : T ) => StrNumSym ) : Record < string , number > {
410409 if ( ! Array . isArray ( array ) ) return { } ;
411410 const result : Record < string , number > = { } ;
412411 for ( const item of array ) {
@@ -598,3 +597,75 @@ export function headOfArr<T>(array: readonly T[]): T | undefined {
598597export function lastOfArr < T > ( array : readonly T [ ] ) : T | undefined {
599598 return Array . isArray ( array ) && array . length > 0 ? array . at ( - 1 ) : undefined ;
600599}
600+
601+ /**
602+ * Drops the first n elements from an array.
603+ *
604+ * @template T
605+ * @param {readonly T[] } array - The source array.
606+ * @param {number } n - Number of elements to drop.
607+ * @returns {T[] } Array with first n elements removed.
608+ *
609+ * @example
610+ * drop([1, 2, 3, 4, 5], 2); // [3, 4, 5]
611+ */
612+ export function drop < T > ( array : readonly T [ ] , n : number ) : T [ ] {
613+ if ( ! Array . isArray ( array ) || n <= 0 ) return [ ...array ] ;
614+ return array . slice ( n ) ;
615+ }
616+
617+ /**
618+ * Drops elements from the start of an array while predicate returns true.
619+ *
620+ * @template T
621+ * @param {readonly T[] } array - The source array.
622+ * @param {(item: T, index: number) => boolean } predicate - Condition function.
623+ * @returns {T[] } Array with elements dropped.
624+ *
625+ * @example
626+ * dropWhile([1, 2, 3, 4, 1], x => x < 3); // [3, 4, 1]
627+ */
628+ export function dropWhile < T > ( array : readonly T [ ] , predicate : ( item : T , index : number ) => boolean ) : T [ ] {
629+ if ( ! Array . isArray ( array ) ) return [ ] ;
630+ let i = 0 ;
631+ while ( i < array . length && predicate ( array [ i ] , i ) ) {
632+ i ++ ;
633+ }
634+ return array . slice ( i ) ;
635+ }
636+
637+ /**
638+ * Finds the element with the maximum value for a given key or function.
639+ *
640+ * @template T
641+ * @param {readonly T[] } array - The source array.
642+ * @param {keyof T | ((item: T) => number) } keyOrFn - Property key or function.
643+ * @returns {T | undefined } Element with maximum value.
644+ *
645+ * @example
646+ * maxBy([{a: 1}, {a: 5}, {a: 3}], 'a'); // {a: 5}
647+ * maxBy([{a: 1}, {a: 5}, {a: 3}], x => x.a); // {a: 5}
648+ */
649+ export function maxBy < T > ( array : readonly T [ ] , keyOrFn : keyof T | ( ( item : T ) => number ) ) : T | undefined {
650+ if ( ! Array . isArray ( array ) || array . length === 0 ) return undefined ;
651+ const fn = typeof keyOrFn === 'function' ? keyOrFn : ( item : T ) => item [ keyOrFn ] as unknown as number ;
652+ return array . reduce < T > ( ( max , item ) => ( fn ( item ) > fn ( max ) ? item : max ) , array [ 0 ] ) ;
653+ }
654+
655+ /**
656+ * Finds the element with the minimum value for a given key or function.
657+ *
658+ * @template T
659+ * @param {readonly T[] } array - The source array.
660+ * @param {keyof T | ((item: T) => number) } keyOrFn - Property key or function.
661+ * @returns {T | undefined } Element with minimum value.
662+ *
663+ * @example
664+ * minBy([{a: 1}, {a: 5}, {a: 3}], 'a'); // {a: 1}
665+ * minBy([{a: 1}, {a: 5}, {a: 3}], x => x.a); // {a: 1}
666+ */
667+ export function minBy < T > ( array : readonly T [ ] , keyOrFn : keyof T | ( ( item : T ) => number ) ) : T | undefined {
668+ if ( ! Array . isArray ( array ) || array . length === 0 ) return undefined ;
669+ const fn = typeof keyOrFn === 'function' ? keyOrFn : ( item : T ) => item [ keyOrFn ] as unknown as number ;
670+ return array . reduce < T > ( ( min , item ) => ( fn ( item ) < fn ( min ) ? item : min ) , array [ 0 ] ) ;
671+ }
0 commit comments