iterable/iterable.ts

import { CheckPromiseIterableType, CheckPromiseReturnType,
  CheckIterableType, CheckPromiseType, PromiseReturnType, PureType } from "../types";
import { isPromise, exec } from "../utils";
import { AsyncIterableType, AsyncIteratorType } from "./asyncIterable";

/**
 * @namespace iterable
 */
/**
 * @memberof iterable
 * @typedef
 */
export type IterableReturnType<T extends Iterable<any>, U> =
  CheckPromiseReturnType<CheckIterableType<T>, U>;

/**
 * @memberof iterable
 * @typedef
 */
export type EachIterableCallbackType<T extends Iterable<any>, U> =
  (value: CheckPromiseIterableType<T>, iterable: T) => U;

/**
 * @memberof iterable
 * @typedef
 */
export type ReduceIterableCallbackType<T, U, R extends Iterable<U | Promise<U>> = Iterable<U>> =
  (previousValue: T, currentValue: U, iterable: R) => T;

export function reduceIterableF<T, U extends Iterable<any>>(
    callbackFn: ReduceIterableCallbackType<T, CheckPromiseIterableType<U>, U>,
    initial: T,
    iterable: U,
  ): IterableReturnType<U, T>;
/**
 * @memberof iterable
 */
export function reduceIterableF<T, U extends Iterable<any>>(
    callbackFn: ReduceIterableCallbackType<T, CheckPromiseIterableType<U>, U>,
    initial: T,
    iterable: U,
  ): Promise<T> | T {
  let cur: T | Promise<T> = initial;

  for (const value of iterable) {
    cur = exec(c => exec(v => callbackFn(c, v, iterable), value), cur);
  }
  return cur;
}
/**
 * @memberof iterable
 */
export function eachIterableF<T extends Iterable<any>>(
  callbackFn: EachIterableCallbackType<T, void>, iterable: T): T {
  for (const value of iterable) {
    exec(v => callbackFn(v, iterable), value);
  }
  return iterable;
}

export function mapIterableF<T extends Iterable<any>, U>(
  callbackFn: EachIterableCallbackType<T, U>, iterable: T):
    CheckPromiseType<CheckIterableType<T>, Iterable<U | Promise<U>>, Iterable<U>>;
/**
 * @memberof iterable
 */
export function* mapIterableF<T extends Iterable<any>, U>(
  callbackFn: EachIterableCallbackType<T, U>, iterable: T): Iterable<U | Promise<U>> {
  for (const value of iterable) {
    yield exec(v => callbackFn(v, iterable), value);
  }
}

/**
 * @memberof iterable
 */
export function* filterIterableF<T extends Iterable<any>>(
  callbackFn: EachIterableCallbackType<T, boolean>,
  iterable: T):
  IterableIterator<CheckPromiseIterableType<T>> {
  for (const value of iterable) {
    exec(v => callbackFn(v, iterable), value) && (yield value);
  }
}

/**
 * @memberof iterable
 */
export function headIterable<T>(iterable: Iterable<T>): T {
  for (const value of iterable) {
    return value;
  }
}

/**
 * @memberof iterable
 */
export function tailIterable<T>(iterable: Iterable<T>): T {
  let cur: T;
  for (const value of iterable) {
    cur = value;
  }
  return cur;
}
export function getIterator<T extends AsyncIterableType<any>>(iterable: T) {
  return ((iterable as any)[Symbol.asyncIterator] || (iterable as any)[Symbol.asyncIterator])();
}
/**
 * @memberof iterable
 */
export function takeIterableF<T>(length: number, iterable: AsyncIterableType<T>):
  PromiseReturnType<Array<PureType<T>>> {

  type t = PureType<T>;
  const cur: t[] = [];

  if (length === 0) {
    return cur;
  }
  let i = 0;

  function _result(result: IteratorResult<T>, iter: AsyncIteratorType<T>): t[] | Promise<T> {
    if (result.done || i >= length) {
      return cur;
    }
    ++i;
    const value = result.value;

    if (isPromise(value)) {
      return value.then(v => {
        cur.push(v);
        return _take(iter);
      });
    } else {
      cur.push(value as any);
    }
    return cur;
  }
  function _take(iter: AsyncIteratorType<T>): any {
    const result = iter.next();

    return exec(r => _result(r, iter), result);
  }
  return _take(getIterator(iterable));
}