import { curry, id } from '@typed/lambda' import { equals, greaterThan, ifElse, lessThan, or } from '@typed/logic' import { decrement, increment } from '@typed/math' /** * Move a value from one index to another * @param fromIndex :: int * @param toIndex :: int * @param list :: [a] * @returns :: [a] */ export const move = curry(__move) as { (fromIndex: number, toIndex: number, list: ArrayLike): A[] (fromIndex: number, toIndex: number): (list: ArrayLike) => A[] (fromIndex: number): { (toIndex: number, list: ArrayLike): A[] (toIndex: number): (list: ArrayLike) => A[] } } function __move(fromIndex: number, toIndex: number, list: ArrayLike): A[] { const length = list.length const newArray = Array(length) if (outOfBounds(length, toIndex) || outOfBounds(length, fromIndex)) { return Array.from(list) } for (let i = 0; i < length; ++i) { newArray[i] = list[findMovedIndex(i, fromIndex, toIndex)] } return newArray } function findMovedIndex(i: number, fromIndex: number, toIndex: number): number { if (equals(i, toIndex)) { return fromIndex } return ifElse( () => lessThan(toIndex, fromIndex), ifElse(between(fromIndex, toIndex), id, increment), ifElse(between(toIndex, fromIndex), id, decrement), i, ) } function outOfBounds(length: number, value: number) { return value < 0 || value >= length } function between(min: number, max: number): (num: number) => boolean { return or(lessThan(min), greaterThan(max)) }