Source: list/indexing.js

/**
 * maryamyriameliamurphies.js
 * A library of Haskell-style morphisms ported to ES2015 JavaScript.
 *
 * list/indexing.js
 *
 * @file Functions for indexing lists.
 * @license ISC
 */

/** @module list/indexing */

import {
  partial,
  $
} from '../base';

import {isEq} from '../eq';

import {listToMaybe} from '../maybe';

import {
  fst,
  snd
} from '../tuple';

import {
  listRange,
  head,
  tail,
  length,
  isList,
  isEmpty,
  map,
  filter,
  zip
} from '../list';

import {error} from '../error';

/**
 * Return the value from a `List` at the specified index, starting at 0.
 * <br>`Haskell> (!!) :: [a] -> Int -> a`
 * @param {List} as - The `List` to index into
 * @param {number} n - The index to return
 * @returns {*} The value at the specified index
 * @kind function
 * @example
 * const lst = list(1,2,3,4,5);
 * index(lst, 3));              // => 4
 */
export const index = (as, n) => {
  const index_ = (as, n) => {
    if (isList(as) === false ) { return error.listError(as, index); }
    if (n < 0) { return error.rangeError(n, index); }
    if (isEmpty(as)) { return error.rangeError(n, index); }
    const x = head(as);
    const xs = tail(as);
    if (n === 0) { return x; }
    return index(xs)(n - 1);
  }
  return partial(index_, as, n);
}

/**
 * Return the index of the first value of a `List` equal to a query value, or `Nothing` if there is
 * no such value.
 * <br>`Haskell> elemIndex :: Eq a => a -> [a] -> Maybe Int`
 * @param {*} n - The query value
 * @param {List} xs - The `List` to evaluate
 * @returns {Maybe} `Just n` or `Nothing`
 * @kind function
 * @example
 * const lst = list(1,2,2,3,2,4,2,2,5,2,6,8);
 * elemIndex(8, lst);                         // => Just 11
 * elemIndex(10, lst);                        // => Nothing
 */
export const elemIndex = (n, xs) => {
  const elemIndex_ = (n, xs) =>
    isList(xs) ? findIndex(isEq(n), xs) : error.listError(xs, elemIndex);
  return partial(elemIndex_, n, xs);
}

/**
 * Return the indices of all values in a `List` equal to a query value, in ascending order.
 * <br>`Haskell> elemIndices :: Eq a => a -> [a] -> [Int]`
 * @param {*} n - The query value
 * @param {List} xs - The `List` to evaluate
 * @returns {List} A `List` of values equal to `n`
 * @kind function
 * @example
 * const lst = list(1,2,2,3,2,4,2,2,5,2,6,8);
 * elemIndices(2, lst);                       // => [1:2:4:6:7:9:[]]
 * elemIndices(10, lst);                      // => [[]]
 */
export const elemIndices = (n, xs) => {
  const elemIndices_ = (n, xs) =>
    isList(xs) ? findIndices(isEq(n), xs) : error.listError(xs, elemIndices);
  return partial(elemIndices_, n, xs);
}

/**
 * Take a predicate function and a `List` and return the first value in the list that satisfies the
 * predicate, or `Nothing` if there is no such element. This function currently only works on `List`
 * objects, but should in the future work for all `Foldable` types.
 * <br>`Haskell> find :: Foldable t => (a -> Bool) -> t a -> Maybe a`
 * @param {Function} p - The predicate function
 * @param {List} xs - The `List` to evaluate
 * @returns {Maybe} The value inside a `Just` or `Nothing`, otherwise
 * @kind function
 * @example
 * const lst = list(1,2,3,4,5,6,7,8,9,10);
 * const pred1 = x => x % 3 === 0;
 * const pred2 = x => x > 10;
 * find(pred1, lst);                       // => Just 3
 * find(pred2, lst);                       // => Nothing
 */
export const find = (p, xs) => {
  const find_ = (p, xs) => isList(xs) ? $(listToMaybe)(filter(p))(xs) : error.listError(xs, find);
  return partial(find_, p, xs);
}

/**
 * Take a predicate function and a `List` and return the index of the first value in the list that
 * satisfies the predicate, or `Nothing` if there is no such element.
 * <br>`Haskell> findIndex :: (a -> Bool) -> [a] -> Maybe Int`
 * @param {Function} p - The predicate function
 * @param {List} xs - The `List` to evaluate
 * @returns {Maybe} The index inside a `Just` or `Nothing`, otherwise
 * @kind function
 * @example
 * const lst = list(1,2,3,4,5,6,7,8,9,10);
 * const pred1 = x => x % 3 === 0;
 * const pred2 = x => x > 10;
 * findIndex(pred1, lst);                  // => Just 2
 * findIndex(pred2, lst);                  // => Nothing
 */
export const findIndex = (p, xs) => {
  const findIndex_ = (p, xs) =>
    isList(xs) ? $(listToMaybe)(findIndices(p))(xs) : error.listError(xs, findIndex);
  return partial(findIndex_, p, xs);
}

/**
 * Return the indices of all values in a `List` that satisfy a given predicate function, in
 * ascending order.
 * <br>`Haskell> findIndices :: (a -> Bool) -> [a] -> [Int]`
 * @param {Function} p - The predicate function
 * @param {List} xs - The `List` to evaluate
 * @returns {List} The `List` of matching indices
 * @kind function
 * @example
 * const lst = list(1,2,3,4,5,6,7,8,9,10);
 * const pred = x => even(x);
 * findIndices(pred, lst);                 // => [1:3:5:7:9:[]]
 */
export const findIndices = (p, xs) => {
  const findIndices_ = (p, xs) => {
    if (isList(xs) === false) { return error.listError(xs, findIndices); }
    const z = zip(xs, listRange(0, length(xs)));
    const f = xs => $(p)(fst)(xs) ? true : false;
    const m = t => snd(t);
    return map(m, filter(f, z));
  }
  return partial(findIndices_, p, xs);
}