git.fiddlerwoaroof.com
Raw Blame History
export function iota(count, start = 0) {
  const result = [];
  for (let x = 0; x < count; x++) {
    result.push(start + x);
  }
  return result;
}

export function over(f, ...args) {
  return function (list) {
    return list.map((v) => f(v, ...args));
  };
}

/**
 * @param {((...args: any[]) => any)[]} funs
 */
export function juxt(...funs) {
  return function (/** @type {any[]} */ ...args) {
    return funs.map((fun) => fun(...args));
  };
}

export function compose(...funs) {
  funs = funs.slice();
  return function (...args) {
    let result = funs.pop()(...args);
    while (funs.length > 0) {
      result = funs.pop()(result);
    }
    return result;
  };
}

export function on(fun, key_fun) {
  return function (data) {
    return fun(key_fun(data));
  };
}

export function applying(f, ...pos_args) {
  return function (list) {
    return f(...pos_args, ...list);
  };
}

export function filter(f, ...args) {
  return function (list) {
    return list.filter((v) => f(v, ...args));
  };
}

export function zip_with(f) {
  return function (lists) {
    const result = [];
    outer: for (let idx = 0; ; idx++) {
      const args = [];
      for (let list of lists) {
        if (idx < list.length) {
          args.push(list[idx]);
        } else {
          break outer;
        }
      }
      result.push(f(...args));
    }
    return result;
  };
}

export function element(key) {
  return function (it) {
    return it[key];
  };
}

export function eq(v, eq) {
  if (eq !== undefined) {
    return function (w) {
      return eq(v, w);
    };
  } else {
    return function (w) {
      return v === w;
    };
  }
}

/**
 * @template T, U
 * @param { (args: T[]) => boolean} cond
 * @param {(args: T[]) => U} fn
 */
export function applicable_when(cond, fn) {
  return function (/** @type {T} */ data) {
    if (cond(data)) {
      return fn(data);
    } else {
      return data;
    }
  };
}

/**
 * @param {{ test: (arg0: any) => any; }} regex
 */
export function matches_regex(regex) {
  return function (string) {
    return regex.test(string);
  };
}

//TODO: handle maps, at least

/**
 * @param {string | number | symbol} key
 */
export function key(key) {
  return function (it) {
    return it[key];
  };
}

/**
 * @param {(...args: any[]) => boolean} fun
 */
export function complement(fun) {
  return function (...args) {
    return !fun(...args);
  };
}

/**
 * @param {(a: any) => boolean} pred
 */
export function include(pred) {
  return function (seq) {
    return seq.filter(pred);
  };
}

/**
 * @param {(a: any, b: any) => boolean} pred
 */
export function exclude(pred) {
  const negated = complement(pred);
  return function (seq) {
    return seq.filter(negated);
  };
}

/**
 * @param {{ [x: string]: any }} keyed
 */
export function pick(keyed) {
  return function (keys) {
    return keys.map((key) => keyed[key]);
  };
}

/**
 * @param {(arg0: any, arg1: any) => any} comparator
 * @param {(arg0: any) => any} key
 */
export function sorted(comparator, key) {
  if (key) {
    return function (seq) {
      const result = seq.slice();
      result.sort((a, b) => {
        return comparator(key(a), key(b)) ? -1 : 1;
      });
      return result;
    };
  } else {
    return function (seq) {
      const result = seq.slice();
      result.sort((a, b) => {
        return comparator(a, b) ? -1 : 1;
      });
      return result;
    };
  }
}

function matching_list_reducer(test, acc, next) {
  let result;
  if (acc && acc[0] && test(acc[0][0], next[0])) {
    result = [
      [acc[0][0], ...[...acc[0].slice(1), ...next.slice(1)]],
      ...acc.slice(1),
    ];
  } else {
    result = [next, ...(acc || [])];
  }
  return result;
}

export function combine_matching_lists({ test = (a, b) => a === b } = {}) {
  return function (acc, next) {
    return matching_list_reducer(test, acc, next);
  };
}

export function cons_new({ test = (a, b) => a === b, key = (v) => v } = {}) {
  return function (acc, next) {
    if (acc && test(key(acc[0]), key(next))) {
      return acc;
    } else {
      return [next, ...acc];
    }
  };
}

/**
 * @param {{ collector?: ({ test, key }?: { test?: (a: any, b: any) => boolean; key?: (v: any) => any; }) => (acc: any, next: any) => any; test: any; key: any; }} [it]
 */
export function compress_runs({ collector = cons_new, test, key } = {}) {
  return function (/** @type {any[]} */ it) {
    return it.reduce(collector({ test, key }), []).reverse();
  };
}

/**
 * @param {number} n
 */
export function slice(n) {
  return function (/** @type {string | any[]} */ seq) {
    return seq.slice(n);
  };
}

/**
 * @param {(arg0: any[]) => any} transform
 */
export function transform_tail(transform) {
  return function ([head, ...tail]) {
    const newTail = transform(tail);
    return [
      head,
      ...(Object.hasOwnProperty.call(newTail, "length") ? newTail : [newTail]),
    ];
  };
}