Codibre/fluent-iterable

View on GitHub
src/recipes/flat-join-recipe.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { tail, head } from './../types/base';
import { isIterable } from '../is-iterable';
import { CombineIngredients } from './ingredients';

function flattable(value: unknown) {
  return typeof value !== 'string' && isIterable(value);
}

function fillField(result: any, field: string, subItem: any) {
  if (!result.hasOwnProperty(field)) {
    result[field] = subItem;
  }
}

function getNewResult(result: any, field: string, subItem: any): any {
  result = {
    ...result,
  };
  fillField(result, field, subItem);
  return result;
}

function flatMapFactory(ingredients: CombineIngredients, deepFlat: Function) {
  return function flatMap(
    result: any,
    item: any,
    path: string[],
    nextIndex: number,
  ): any {
    const { flatten } = ingredients;
    const start = nextIndex;

    let current: any = item;
    for (let i = start; i < path.length; i++) {
      const field = path[i];
      current = current[field];
      if (flattable(current)) {
        return flatten.call(deepFlat(current), (subItem: any) =>
          flatMap(getNewResult(result, field, subItem), subItem, path, i + 1),
        );
      }
      fillField(result, field, current);
    }
    result[head] = current;

    return [result];
  };
}

export function flatJoinRecipe(ingredients: CombineIngredients) {
  const { flatten } = ingredients;
  const deepFlat = (x: Iterable<any>): any =>
    flatten.call(x, (y: any) => (flattable(y) ? deepFlat(y) : [y]));
  const flatMap = flatMapFactory(ingredients, deepFlat);
  return function <T, R>(this: Iterable<T>, ...path: string[]): Iterable<R> {
    function process(y: any): any {
      return flatMap({ [tail]: y }, y, path, 0);
    }

    return flatten.call(this, (x) =>
      flattable(x) ? flatten.call(deepFlat(x), process) : process(x),
    ) as any;
  };
}