array/array.ts

import { PromiseReturnType,
  CheckPromiseType, CheckIterableType,
  CheckPromiseIterableType, Curry2, Curry1 } from "../types";
import { exec, curry } from "../utils";

/**
* @namespace array
*/
/**
* @memberof array
* @typedef
*/
export type EachArrayCallbackType<T, U, R extends Array<T | Promise<T>> = T[]> =
  (value: T, key: number, iterator: R) => U;

/**
* @memberof array
* @typedef
*/
export type ReduceArrayCallbackType<T, U, R extends Array<U | Promise<U>> = U[]> =
  (previousValue: T, currentValue: U, currentIndex: number, arr: R) => T;
/**
* @memberof array
* @typedef
*/
export type ArrayPromiseReturnType<T extends any[]> = CheckPromiseType<
  CheckIterableType<T>,
  PromiseReturnType<Array<CheckPromiseIterableType<T>>>,
  Array<CheckPromiseIterableType<T>>>;
/**
 * @memberof array
 */
export function eachArrayF<T>(f: EachArrayCallbackType<T, void>, arr: T[]): T[] {
  arr.forEach(f);
  return arr;
}

/**
 * @memberof array
 * @function
 */
export const eachArray:
  (<T = any>(f: EachArrayCallbackType<T, void>) => <U extends T>(arr: U[]) => U[]) &
  (<T = any>(f: EachArrayCallbackType<T, void>, arr: T[]) => T[]) =
  /*#__PURE__*/curry(eachArrayF);

/**
 * @memberof array
 */
export function mapArrayF<T, U>(f: EachArrayCallbackType<T, U>, arr: T[]): U[] {
  return arr.map(f);
}
/**
 * @memberof array
 * @function
 */
export const mapArray:
 (<T = any, U = any>(f: EachArrayCallbackType<T, U>) => <A extends T, B extends U>(arr: A[]) => B[]) &
 (<T = any, U = any>(f: EachArrayCallbackType<T, U>, arr: T[]) => U[]) =
 /*#__PURE__*/curry(mapArrayF);
/**
 * @memberof array
 */
export function reduceArrayF<T, U>(
  callbackFn: ReduceArrayCallbackType<T, U>,
  initial: T,
  arr: U[]): T {
  return arr.reduce(callbackFn, initial);
}

/**
 * @memberof array
 * @function
 */
export const reduceArray:
  (<T, U>(callbackFn: ReduceArrayCallbackType<T, U>) => Curry2<T, U[], T>) &
  (<T, U>(callbackFn: ReduceArrayCallbackType<T, U>, initial: T) => Curry1<U[], T>) &
  (<T, U>(callbackFn: ReduceArrayCallbackType<T, U>, initial: T, arr: U[]) => T) =
  /*#__PURE__*/curry(reduceArrayF);

/**
 * @memberof array
 */
export function filterArrayF<T>(f: EachArrayCallbackType<T, boolean>, arr: T[]): T[] {
  return arr.filter(f);
}

/**
 * @memberof array
 * @function
 */
export const filterArray:
 (<T = any>(f: EachArrayCallbackType<T, boolean>) => Curry1<T[], T[]>) &
 (<T = any>(f: EachArrayCallbackType<T, boolean>, arr: T[]) => T[]) =
 /*#__PURE__*/ curry(filterArrayF);

/**
 * @memberof array
 */
export function headArray<T>(iterator: T[]): T {
  return iterator[0];
}

/**
 * @memberof array
 */
export function tailArray<T>(iterator: T[]): T {
  return iterator[iterator.length - 1];
}

export function takeArrayF<T extends any[]>(length: number, arr: T):
  ArrayPromiseReturnType<T>;
/**
 * @memberof array
 */
export function takeArrayF<T extends any[]>(length: number, arr: T) {
  const arrLength = Math.min(length, arr.length);
  const arr2 = arr.slice(0, arrLength);

  return reduceArrayF((prev, cur) => {
    return exec(p => exec(c => {
      p.push(c);
      return p;
    }, cur), prev);
  }, [] as T | Promise<T>, arr2);
}

/**
 * @memberof array
 * @function
 */
export const takeArray:
(<T extends any[]>(length: number) =>
  <U extends T>(arr: U) => ArrayPromiseReturnType<U>) &
 (<T extends any[]>(length: number, arr: T) => ArrayPromiseReturnType<T>) =
 /*#__PURE__*/ curry(takeArrayF);