/** * @description Convert an iterable target into an array using spread. * * @example * ``` * // Expect: [1, 2, 3] * const example1 = arrayFromSpread(new Set([1, 2, 3])) * // Expect: [] * const example2 = arrayFromSpread([]) * ``` */ export const arrayFromSpread = (target: Iterable): T[] => { return [...target] } /** * @description Returns the length of `targetArray`. * * @example * ``` * // Expect: 3 * const example1 = arrayLength(["a", "b", "c"]) * // Expect: 0 * const example2 = arrayLength([]) * ``` */ export const arrayLength = (targetArray: unknown[]): number => { return targetArray.length } /** * @description Returns a copy of `targetArray` in which last element has been removed. * New array's length is `targetArray.length - 1`. * * @example * ``` * // Expect: [1, 2] * const example1 = arrayPop([1, 2, 3]) * // Expect: [] * const example2 = arrayPop(["only"]) * ``` */ export const arrayPop = (targetArray: T[]): T[] => { return targetArray.slice(0, -1) } /** * @description Returns a copy of `targetArray` in which inserts the given `item` as new last item. * New array's length is `targetArray.length + 1`. * * @example * ``` * // Expect: [1, 2, 3] * const example1 = arrayPush(3, [1, 2]) * // Expect: ["a"] * const example2 = arrayPush("a", []) * ``` */ export const arrayPush = (item: T, targetArray: T[]): T[] => { return [...targetArray, item] } /** * @description Alias of {@link arrayPush}. * * @example * ``` * // Expect: [1, 2] * const example1 = arrayAppend(2, [1]) * // Expect: ["x"] * const example2 = arrayAppend("x", []) * ``` */ export const arrayAppend: typeof arrayPush = arrayPush /** * @description Returns a copy of `targetArray` in which inserts the given `item` as new fist item. * New array's length is `targetArray`'s length + 1. * * @example * ``` * // Expect: [0, 1, 2] * const example1 = arrayUnshift(0, [1, 2]) * // Expect: ["a"] * const example2 = arrayUnshift("a", []) * ``` */ export const arrayUnshift = (item: T, targetArray: T[]): T[] => { return [item, ...targetArray] } /** * @description Alias of {@link arrayUnshift}. * * @example * ``` * // Expect: ["first", "second"] * const example1 = arrayPrepend("first", ["second"]) * // Expect: [true] * const example2 = arrayPrepend(true, []) * ``` */ export const arrayPrepend: typeof arrayUnshift = arrayUnshift /** * @description Returns a copy of `targetArray` in which first element has been removed. * New array's length is `targetArray`'s length - 1. * * @example * ``` * // Expect: [2, 3] * const example1 = arrayShift([1, 2, 3]) * // Expect: [] * const example2 = arrayShift(["only"]) * ``` */ export const arrayShift = (targetArray: T[]): T[] => { return targetArray.slice(1) } /** * @description Returns the first element of `targetArray`. * * @example * ``` * // Expect: 1 * const example1 = arrayHead([1, 2, 3]) * // Expect: undefined * const example2 = arrayHead([]) * ``` * * @see {@link arrayTail}, {@link arrayLast} */ export const arrayHead = (targetArray: T[]): T | undefined => { return targetArray[0] } /** * @description Returns the tail of `targetArray`. * * @example * ``` * // Expect: [2, 3] * const example1 = arrayTail([1, 2, 3]) * // Expect: [] * const example2 = arrayTail(["only"]) * ``` * * @see {@link arrayHead}, {@link arrayInit} */ export const arrayTail = (targetArray: T[]): T[] => { return targetArray.slice(1) } /** * @description Returns the init of `targetArray`. * * @example * ``` * // Expect: [1, 2] * const example1 = arrayInit([1, 2, 3]) * // Expect: [] * const example2 = arrayInit(["only"]) * ``` * * @see {@link arrayLast}, {@link arrayTail} */ export const arrayInit = (targetArray: T[]): T[] => { return targetArray.slice(0, -1) } /** * @description Returns the last element of `targetArray`. * * @example * ``` * // Expect: 3 * const example1 = arrayLast([1, 2, 3]) * // Expect: undefined * const example2 = arrayLast([]) * ``` * * @see {@link arrayInit}, {@link arrayHead} */ export const arrayLast = (targetArray: T[]): T | undefined => { return targetArray.at(-1) } /** * @description Returns a copy of a section of an array. * * @example * ``` * // Expect: ["b", "c"] * const example1 = arraySlice(1, 3, ["a", "b", "c", "d"]) * // Expect: [] * const example2 = arraySlice(2, 2, [1, 2, 3]) * ``` * * @see {@link arraySliceInit}, {@link arraySliceTail} */ export const arraySlice = (start: number, end: number, targetArray: T[]): T[] => { return targetArray.slice(start, end) } /** * @description Returns a copy of a section of an array. * * @example * ``` * // Expect: [3, 4] * const example1 = arraySliceTail(2, [1, 2, 3, 4]) * // Expect: [] * const example2 = arraySliceTail(1, ["only"]) * ``` * * @see {@link arraySlice}, {@link arraySliceTail} * @see {@link arrayDrop} */ export const arraySliceTail = (start: number, targetArray: T[]): T[] => { return targetArray.slice(start) } /** * @description Returns a copy of a section of an array. * * @example * ``` * // Expect: [3, 4] * const example1 = arrayDrop(2, [1, 2, 3, 4]) * // Expect: [] * const example2 = arrayDrop(-2, [1, 2]) * ``` * * @see {@link arraySliceTail} */ export const arrayDrop = (n: number, targetArray: T[]): T[] => { return targetArray.slice(Math.abs(Math.round(n))) } /** * @description Returns a copy of a section of an array. * * @example * ``` * // Expect: [1, 2] * const example1 = arraySliceInit(2, [1, 2, 3, 4]) * // Expect: [] * const example2 = arraySliceInit(0, [1, 2]) * ``` * * @see {@link arraySlice}, {@link arraySliceInit} */ export const arraySliceInit = (end: number, targetArray: T[]): T[] => { return targetArray.slice(0, Math.round(end)) } /** * @description Returns a copy of a section of an array. * * @example * ``` * // Expect: [1, 2] * const example1 = arrayDropLast(2, [1, 2, 3, 4]) * // Expect: [] * const example2 = arrayDropLast(3, [1, 2]) * ``` * * @see {@link arraySliceInit} */ export const arrayDropLast = (n: number, targetArray: T[]): T[] => { return targetArray.slice(0, -Math.abs(Math.round(n))) } /** * @description Returns a copy of `targetArray` with the elements between `start` (inclusive) * and `end` (exclusive) removed. * * @example * ``` * // Expect: ["a", "d"] * const example1 = arrayRemove(1, 3, ["a", "b", "c", "d"]) * // Expect: [1, 2, 3] * const example2 = arrayRemove(2, 2, [1, 2, 3]) * ``` */ export const arrayRemove = (start: number, end: number, targetArray: T[]): T[] => { let internalStart = Math.abs(Math.round(start)) let internalEnd = Math.abs(Math.round(end)) if (internalEnd < internalStart) { ;[internalStart, internalEnd] = [internalEnd, internalStart] } return [...targetArray.slice(0, internalStart), ...targetArray.slice(internalEnd)] } /** * @description Returns a copy of `targetArray` with the element at `index` removed. * * @example * ``` * // Expect: ["a", "c"] * const example1 = arrayRemoveIndex(1, ["a", "b", "c"]) * // Expect: [] * const example2 = arrayRemoveIndex(0, ["only"]) * ``` */ export const arrayRemoveIndex = (index: number, targetArray: T[]): T[] => { return [ ...targetArray.slice(0, index), ...targetArray.slice(index + 1 === 0 ? Infinity : index + 1), ] } /** * @description Combines two array. `appendedArray` will be appended to the end of `targetArray`. * * @example * ``` * // Expect: [1, 2, 3, 4] * const example1 = arrayConcat([3, 4], [1, 2]) * // Expect: ["a"] * const example2 = arrayConcat([], ["a"]) * ``` */ export const arrayConcat = (appendedArray: T[], targetArray: T[]): T[] => { return [...targetArray, ...appendedArray] } /** * @description Adds all the elements of an array into a string, separated by the specified * separator string. * * @example * ``` * // Expect: "a,b,c" * const example1 = arrayJoin(",", ["a", "b", "c"]) * // Expect: "" * const example2 = arrayJoin("-", []) * ``` */ export const arrayJoin = (separator: string, targetArray: unknown[]): string => { return targetArray.join(separator) } /** * @description Returns the first index at which a given element can be found in the array, * or -1 if it is not present. * * @example * ``` * // Expect: 1 * const example1 = arrayIndexOf("b", ["a", "b", "c"]) * // Expect: -1 * const example2 = arrayIndexOf(9, [1, 2, 3]) * ``` */ export const arrayIndexOf = (searchElement: unknown, targetArray: unknown[]): number => { return targetArray.indexOf(searchElement) } /** * @description Returns the last index at which a given element can be found in the array, * or -1 if it is not present. * * @example * ``` * // Expect: 2 * const example1 = arrayLastIndexOf("a", ["a", "b", "a"]) * // Expect: -1 * const example2 = arrayLastIndexOf(9, [1, 2, 3]) * ``` */ export const arrayLastIndexOf = (searchElement: unknown, targetArray: unknown[]): number => { return targetArray.lastIndexOf(searchElement) } /** * @description Predicate whether the target array includes the searchElement. * * @example * ``` * // Expect: true * const example1 = arrayIncludes(2, [1, 2, 3]) * // Expect: false * const example2 = arrayIncludes("x", ["a", "b"]) * ``` */ export const arrayIncludes = (searchElement: T, targetArray: T[]): boolean => { return targetArray.includes(searchElement) } /** * @description Predicate whether all elements in the target array satisfy the provided testing function. * * @example * ``` * // Expect: true * const example1 = arrayEvery((item) => item > 0, [1, 2, 3]) * // Expect: false * const example2 = arrayEvery((item) => item > 1, [1, 2, 3]) * ``` */ export function arrayEvery( predicate: (item: T, index: number, array: T[]) => item is S, targetArray: T[], ): targetArray is S[] export function arrayEvery( predicate: (item: T, index: number, array: T[]) => boolean, targetArray: T[], ): boolean export function arrayEvery( predicate: (item: T, index: number, array: T[]) => boolean, targetArray: T[], ): boolean { return targetArray.every((item, index, array) => predicate(item, index, array)) } /** * @description Alias of {@link arrayEvery}. * * @example * ``` * // Expect: true * const example1 = arrayAll((item) => item !== null, [1, 2]) * // Expect: false * const example2 = arrayAll((item) => item === 0, [0, 1]) * ``` */ export const arrayAll: typeof arrayEvery = arrayEvery /** * @description Predicate whether at least one element in the target array satisfies the provided testing * function. * * @example * ``` * // Expect: true * const example1 = arraySome((item) => item > 2, [1, 2, 3]) * // Expect: false * const example2 = arraySome((item) => item < 0, [1, 2, 3]) * ``` */ export const arraySome = ( predicate: (item: T, index: number, array: T[]) => boolean, targetArray: T[], ): boolean => { return targetArray.some((item, index, array) => predicate(item, index, array)) } /** * @description Alias of {@link arraySome}. * * @example * ``` * // Expect: true * const example1 = arrayAny((item) => item === "b", ["a", "b"]) * // Expect: false * const example2 = arrayAny((item) => item === "x", ["a", "b"]) * ``` */ export const arrayAny: typeof arraySome = arraySome /** * @description Applies a function to each element in the target array. * * @example * ``` * const target: number[] = [] * arrayForEach((item) => target.push(item * 2), [1, 2, 3]) * // Expect: [2, 4, 6] * const example1 = target * ``` */ export const arrayForEach = ( eachDo: (item: T, index: number, array: T[]) => void, targetArray: T[], ): void => { targetArray.forEach((item, index, array) => { eachDo(item, index, array) }) } /** * @description Returns a new array containing all elements of the target array that satisfy the provided testing * function. * * @example * ``` * // Expect: [2, 4] * const example1 = arrayFilter((item) => item % 2 === 0, [1, 2, 3, 4]) * // Expect: [] * const example2 = arrayFilter((item) => item > 0, []) * ``` * * @see {@link arrayReject} */ export function arrayFilter( predicate: (item: T, index: number, array: T[]) => item is S, targetArray: T[], ): S[] export function arrayFilter( predicate: (item: T, index: number, array: T[]) => boolean, targetArray: T[], ): T[] export function arrayFilter( predicate: (item: T, index: number, array: T[]) => boolean, targetArray: T[], ): T[] { return targetArray.filter((item, index, array) => predicate(item, index, array)) } /** * @description Returns a new array containing all elements of the target array that do not satisfy the provided * testing function. * * @example * ``` * // Expect: [1, 3] * const example1 = arrayReject((item) => item % 2 === 0, [1, 2, 3, 4]) * // Expect: ["a"] * const example2 = arrayReject((item) => item === "b", ["a", "b"]) * ``` * * @see {@link arrayFilter} */ export const arrayReject = ( predicate: (item: T, index: number, array: T[]) => boolean, targetArray: T[], ): T[] => { return targetArray.filter((item, index, array) => !predicate(item, index, array)) } /** * @description Returns a tuple of two arrays: the first array contains the elements of the target array that * satisfy the provided testing function, while the second array contains the elements that do not * satisfy the testing function. * * @example * ``` * // Expect: [[2, 4], [1, 3]] * const example1 = arrayPartition((item) => item % 2 === 0, [1, 2, 3, 4]) * // Expect: [[], ["a"]] * const example2 = arrayPartition((item) => item === "b", ["a"]) * ``` * * @see {@link arrayFilter}, {@link arrayReject} */ export const arrayPartition = ( predicate: (item: T, index: number, array: T[]) => boolean, targetArray: T[], ): [T[], T[]] => { const truthyArray: T[] = [] const falsyArray: T[] = [] targetArray.forEach((item, index, array) => { if (predicate(item, index, array)) { truthyArray.push(item) } else { falsyArray.push(item) } }) return [truthyArray, falsyArray] } /** * @description Returns a new array containing the results of applying a provided function to every element in * the target array. * * @example * ``` * // Expect: [2, 4, 6] * const example1 = arrayMap((item) => item * 2, [1, 2, 3]) * // Expect: [] * const example2 = arrayMap((item) => item, []) * ``` */ export const arrayMap = ( transformation: (item: T, index: number, array: T[]) => R, targetArray: T[], ): R[] => { return targetArray.map((item, index, array) => transformation(item, index, array)) } /** * @description Returns a new array with all sub-array elements concatenated into it recursively up to the * specified depth. * * @example * ``` * // Expect: [1, 2, 3, 4] * const example1 = arrayFlat(1, [1, [2, 3], [4]]) * // Expect: [1, 2] * const example2 = arrayFlat(2, [1, [[2]]]) * ``` * * @see {@link arrayFlatMap} */ export const arrayFlat = ( depth: D, targetArray: T[], ): Array> => { return targetArray.flat(depth) } /** * @description Returns a new array containing the results of applying a provided function to every element in * the target array, and then flattening the result by one level. * * @example * ``` * // Expect: [1, 1, 2, 2] * const example1 = arrayFlatMap((item) => [item, item], [1, 2]) * // Expect: [] * const example2 = arrayFlatMap((item) => [item], []) * ``` * * @see {@link arrayMap}, {@link arrayFlat} */ export const arrayFlatMap = ( transformation: (item: T, index: number, array: T[]) => R | R[], targetArray: T[], ): R[] => { return targetArray.flatMap((item, index, array) => transformation(item, index, array)) } /** * @description Returns a single value that is the result of applying a provided function against an accumulator * and each element in the target array (from left to right). * * @example * ``` * // Expect: 6 * const example1 = arrayReduce((acc, item) => acc + item, 0, [1, 2, 3]) * // Expect: "abc" * const example2 = arrayReduce((acc, item) => acc + item, "", ["a", "b", "c"]) * ``` * * @see {@link arrayReduceLeft}, {@link arrayReduceRight} */ export const arrayReduce = ( reducer: (accumulated: R, item: T, index: number, array: T[]) => R, initialValue: R, targetArray: T[], ): R => { return targetArray.reduce( (accumulated, item, index, array) => reducer(accumulated, item, index, array), initialValue, ) } /** * @description Alias of {@link arrayReduce}. * * @example * ``` * // Expect: 6 * const example1 = arrayReduceLeft((acc, item) => acc + item, 0, [1, 2, 3]) * // Expect: "ab" * const example2 = arrayReduceLeft((acc, item) => acc + item, "", ["a", "b"]) * ``` * * @see {@link arrayReduce} */ export const arrayReduceLeft: typeof arrayReduce = arrayReduce /** * @description Returns a single value that is the result of applying a provided function against an accumulator * and each element in the target array (from right to left). * * @example * ``` * // Expect: "cba" * const example1 = arrayReduceRight((acc, item) => acc + item, "", ["a", "b", "c"]) * // Expect: 6 * const example2 = arrayReduceRight((acc, item) => acc + item, 0, [1, 2, 3]) * ``` * * @see {@link arrayReduceLeft}, {@link arrayReduceRight} */ export const arrayReduceRight = ( reducer: (accumulated: R, item: T, index: number, array: T[]) => R, initialValue: R, targetArray: T[], ): R => { return targetArray.reduceRight( (accumulated, item, index, array) => reducer(accumulated, item, index, array), initialValue, ) } /** * @description Returns a new array containing only one copy of each element in the original array. * * @example * ``` * // Expect: [1, 2, 3] * const example1 = arrayUnique([1, 2, 1, 3]) * // Expect: ["a"] * const example2 = arrayUnique(["a", "a"]) * ``` */ export const arrayUnique = (targetArray: T[]): T[] => { return Array.from(new Set(targetArray)) } /** * @description Returns a new array containing only one copy of each element in the original array, * based on the provided unique function. * * @example * ``` * // Expect: [{ id: 1 }, { id: 2 }] * const example1 = arrayUniqueBy((item) => item.id, [{ id: 1 }, { id: 1 }, { id: 2 }]) * // Expect: [] * const example2 = arrayUniqueBy((item) => item, []) * ``` */ export const arrayUniqueBy = ( uniqueFunction: (item: T) => unknown, targetArray: T[], ): T[] => { const uniqueMap = new Map() targetArray.forEach((item) => { const uniqueKey = uniqueFunction(item) if (!uniqueMap.has(uniqueKey)) { uniqueMap.set(uniqueKey, item) } }) return Array.from(uniqueMap.values()) } /** * @description Combines two arrays into one array (no duplicates) composed of the elements of each array. * * @example * ``` * // Expect: [1, 2, 3] * const example1 = arrayUnion([1, 2], [2, 3]) * // Expect: ["a"] * const example2 = arrayUnion(["a"], []) * ``` * * @see {@link arrayIntersection} */ export const arrayUnion = (firstArray: T[], secondArray: T[]): T[] => { return arrayUnique([...firstArray, ...secondArray]) } type CompareArray = (arr1: unknown[], arr2: unknown[]) => unknown[] const _longer: CompareArray = (arr1, arr2) => (arr1.length > arr2.length ? arr1 : arr2) const _shorter: CompareArray = (arr1, arr2) => (arr1.length > arr2.length ? arr2 : arr1) /** * @description Combines two arrays into one array (no duplicates) composed of those elements common to both * arrays. * * @example * ``` * // Expect: [2] * const example1 = arrayIntersection([1, 2], [2, 3]) * // Expect: [] * const example2 = arrayIntersection(["a"], ["b"]) * ``` * * @see {@link https://ramdajs.com/docs/#intersection} * @see {@link arrayUnion} */ export const arrayIntersection = (firstArray: T[], secondArray: T[]): T[] => { // reference: ramda, it is more efficient when the array length gap is large const lookupArr = _longer(firstArray, secondArray) const filteredArr = _shorter(firstArray, secondArray) return arrayUnique(filteredArr.filter((item) => lookupArr.includes(item))) as T[] } /** * @description Returns a new array with the elements in reverse order. * * @example * ``` * // Expect: [3, 2, 1] * const example1 = arrayReverse([1, 2, 3]) * // Expect: [] * const example2 = arrayReverse([]) * ``` */ export const arrayReverse = (targetArray: T[]): T[] => { return targetArray.toReversed() } /** * @description Returns a new array with the elements sorted according to the provided compare function. * * @example * ``` * // Expect: [1, 2, 3] * const example1 = arraySort((a, b) => a - b, [3, 1, 2]) * // Expect: ["a", "b"] * const example2 = arraySort((a, b) => a.localeCompare(b), ["b", "a"]) * ``` */ export const arraySort = ( compareFunction: (a: T, b: T) => number, targetArray: T[], ): T[] => { return targetArray.toSorted(compareFunction) } /** * @description Shuffle the target array. * * @example * ``` * const example1 = shuffle([1, 2, 3]) * const example2 = shuffle(["a", "b"]) * // Expect: example1.length === 3 * // Expect: example2.length === 2 * ``` * * @see {@link https://github.com/mqyqingfeng/Blog/issues/51} */ export const shuffle = (target: T[]): T[] => { const _target = [...target] for (let i = _target.length; i !== 0; i = i - 1) { const j = Math.floor(Math.random() * i) // @ts-expect-error - ignore the error ;[_target[i - 1], _target[j]] = [_target[j], _target[i - 1]] } return _target } /** * @description Applies a function to the value at the given index of an array, * returning a new copy of the array with the element at the given index * replaced with the result of the function application. * * @example * ``` * // Expect: [1, 20, 3] * const example1 = arrayAdjust(1, (item) => item * 10, [1, 2, 3]) * // Expect: ["a"] * const example2 = arrayAdjust(0, (item) => item, ["a"]) * ``` * * @see {@link https://ramdajs.com/docs/#adjust} */ export const arrayAdjust = ( index: number, transformation: (item: T) => T, targetArray: T[], ): T[] => { const _targetArray = [...targetArray] _targetArray[index] = transformation(_targetArray[index] as T) return _targetArray } /** * @description Returns a new copy of the array with the element * at the provided index replaced with the given value. * * @example * ``` * // Expect: [1, 9, 3] * const example1 = arrayUpdate(1, 9, [1, 2, 3]) * // Expect: ["x"] * const example2 = arrayUpdate(0, "x", ["a"]) * ``` * * @see {@link arrayAdjust} */ export const arrayUpdate = (index: number, newItem: T, targetArray: T[]): T[] => { return arrayAdjust(index, () => newItem, targetArray) }