MartyO256/find-files-by-patterns

View on GitHub
src/iterable.ts

Summary

Maintainability
B
4 hrs
Test Coverage
/**
 * Determines whether or not a given non-null object is of type iterable. It
 * cannot however determine the type of values that are iterated by the object's
 * iterator.
 * @typeparam T The type of iterated elements.
 * @param object The non-null object to check.
 * @returns Whether or not the given object is of type iterable.
 */
export const isIterable = <T>(object): object is Iterable<T> =>
  typeof object[Symbol.iterator] === "function";

/**
 * Determines whether or not a given non-null object is of type asynchronous
 * iterable. It cannot however determine the type of values that are iterated by
 * the object's asynchronous iterator.
 * @typeparam T The type of iterated elements.
 * @param object The non-null object to check.
 * @returns Whether or not the given object is of type asynchronous iterable.
 */
export const isAsyncIterable = <T>(object): object is AsyncIterable<T> =>
  typeof object[Symbol.asyncIterator] === "function";

/**
 * Retrieves the first element of an iterable.
 * @typeparam T The type of iterated elements.
 * @param iterable The iterable from which to retrieve the first element.
 * @return The first element of the given iterable.
 */
export const firstElement = async <T>(
  iterable: Iterable<T> | AsyncIterable<T>,
): Promise<T | null> => {
  for await (const element of iterable) {
    return element;
  }
  return null;
};

/**
 * Retrieves the first element of an iterable.
 * @typeparam T The type of iterated elements.
 * @param iterable The iterable from which to retrieve the first element.
 * @return The first element of the given iterable.
 */
export const firstElementSync = <T>(iterable: Iterable<T>): T | null => {
  for (const element of iterable) {
    return element;
  }
  return null;
};

/**
 * A conflict error occurs when multiple elements should not be yielded by an
 * iterable.
 * @typeparam T The type of conflicting elements.
 */
export interface ConflictError<T> extends Error {
  /**
   * The conflicting elements which are the cause of the error.
   */
  conflicts: T[];
}

/**
 * Constructs an error for conflicting elements.
 * @typeparam T The type of conflicting elements.
 * @param conflicts The conflicting elements.
 * @returns The error to throw in case of conflicting elements.
 */
const conflictingElementsError = <T>(...conflicts: T[]): ConflictError<T> => ({
  ...new Error(`The following elements are conflicting: ${conflicts}`),
  conflicts,
});

/**
 * Retrieves the first and only element of an iterable.
 * @typeparam T The type of iterated elements.
 * @param iterable The iterable from which to retrieve the first and only
 * element.
 * @throws If there is more than one element in the iterable.
 * @return The first and only element of the given iterable.
 */
export const onlyElement = async <T>(
  iterable: Iterable<T> | AsyncIterable<T>,
): Promise<T | null> => {
  let retainedElement: T;
  for await (const element of iterable) {
    if (retainedElement === undefined) {
      retainedElement = element;
    } else {
      throw conflictingElementsError(retainedElement, element);
    }
  }
  return retainedElement;
};

/**
 * Retrieves the first and only element of an iterable.
 * @typeparam T The type of iterated elements.
 * @param iterable The iterable from which to retrieve the first and only
 * element.
 * @throws If there is more than one element in the iterable.
 * @return The first and only element of the given iterable.
 */
export const onlyElementSync = <T>(iterable: Iterable<T>): T | null => {
  let retainedElement: T;
  for (const element of iterable) {
    if (retainedElement === undefined) {
      retainedElement = element;
    } else {
      throw conflictingElementsError(retainedElement, element);
    }
  }
  return retainedElement;
};

/**
 * Retrieves all the elements of an iterable. The iterable should be finite.
 * @typeparam T The type of iterated elements.
 * @param iterable The iterable from which to retrieve all the elements.
 * @return The array of all the elements of the given iterable in sequential
 * order.
 */
export const allElements = async <T>(
  iterable: Iterable<T> | AsyncIterable<T>,
): Promise<T[]> => {
  const array: T[] = [];
  for await (const element of iterable) {
    array.push(element);
  }
  return array;
};

/**
 * Retrieves all the elements of an iterable. The iterable should be finite.
 * @typeparam T The type of iterated elements.
 * @param iterable The iterable from which to retrieve all the elements.
 * @return The array of all the elements of the given iterable in sequential
 * order.
 */
export const allElementsSync = <T>(iterable: Iterable<T>): T[] => [...iterable];