All files / string/src characterSequence.ts

100% Statements 10/10
100% Branches 10/10
100% Functions 3/3
100% Lines 9/9

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 621x 1x 1x   1x                                                                         5x                     5x 5x     5x 18x        
import { pipe } from "@benneq/function";
import { map, takeWhile } from "@benneq/iterable";
import { numberSequence } from "@benneq/number";
import { CharCodeRange } from "./_types";
import { charCodeOf } from "./charCodeOf";
 
/**
 * Iterates over a range of characters.
 *
 * Only the first character of the `initialCharacter` will be used.
 *
 * If `initialCharacter` is empty, the sequence will start with `rangeStart` if
 * `stepSize` is positive, or `rangeEnd` if `stepSize` is negative.
 *
 * If `stepSize` is `0` the sequence is empty.
 *
 * @example
 * Iterate over `uppercaseAsciiLetterRange`
 * ```ts
 * for (const c of characterSequence(uppercaseAsciiLetterRange)) {
 *   console.log(c); // 'A','B',...,'Z'
 * }
 * ```
 *
 * @example
 * Start at letter 'D' and iterate backwards
 * ```ts
 * for (const c of characterSequence(uppercaseAsciiLetterRange, 'D', -1)) {
 *   console.log(c); // 'D','C','B','A'
 * }
 * ```
 *
 * @example
 * Create a character Array
 * ```ts
 * const a = [...characterSequence(uppercaseAsciiLetterRange, 'V', 2)];
 * console.log(a); // ['V', 'X', 'Z']
 * ```
 *
 * @returns an Iterable that iterates through the characters
 */
export const characterSequence = (
  [rangeStart, rangeEnd]: CharCodeRange,
  initialCharacter = "",
  stepSize = 1,
  // internal variables:
  charCode = initialCharacter
    ? charCodeOf(initialCharacter)
    : stepSize < 0
    ? rangeEnd
    : rangeStart
): Iterable<string> => {
  if (process.env.NODE_ENV !== "production") {
    console.assert(stepSize >= 1);
  }
 
  return pipe(
    takeWhile<number>((value) => value >= rangeStart && value <= rangeEnd),
    map(String.fromCharCode)
  )(numberSequence(charCode, stepSize));
};