Source: index.js

/**
 * @file Iterator tools
 * @version v0.0.3
 * @author Adam Mill <hismajesty@theroyalwhee.com>
 * @copyright Copyright 2020-2021 Adam Mill
 * @license Apache-2.0
 */

/**
 * Constants.
 * @ignore
 */
const MAX = Number.MAX_SAFE_INTEGER;

/**
 * Generator a range sequence.
 * @example
 * range(0, 10);     // From 0-10 step 1 (auto)
 * range(10, 0);     // From 10-0 step -1 (auto)
 * range(20);        // From 0-20 step 1 (auto)
 * range(3, 12, 3);  // From 3-12 step 3
 * @param {integer} from Starting value, inclusive.
 * @param {integer} to Ending value value, inclusive.
 * @param {integer} step The amount to advance by.
 * @yields {integer} The next step in the range.
 */
function* range(from, to, step) {
  if(from !== undefined && to === undefined) {
    to = from;
    from = 0;
  }
  if(from === undefined) {
    from = 0;
  }
  if(to === undefined) {
    to = MAX;
  };
  if(step === undefined) {
    step = from < to ? 1 : -1;
  }
  const lt = step > 0;
  let current = from;
  while(lt ? current <= to : current >= to) {
    yield current;
    current += step;
  }
}

/**
 * Repeat a value.
 * @param {any} value Any value to repeat.
 * @param {integer} length Number of times to repat.
 * @yields {any} The repeated value.
 */
function* repeat(value, length=MAX) {
  let idx = 0;
  while(idx < length) {
    yield value;
    idx += 1;
  }
}

/**
 * Limit wrapped iterator to a given length.
 * @param {iterable} iter The iterable to operator on.
 * @param {integer} length The length to limit wrapper iterable to.
 * @returns The iterable limited to specified length.
 */
function* limit(iter, length) {
  let idx = 0;
  for(let value of iter) {
    if(idx >= length) {
      break;
    }
    yield value;
    idx += 1;
  }
}

/**
 * Concatinate iterators together.
 * @param  {...iterables} iters The iterators to concatinate.
 * @returns The resulting concatinated iterator.
 */
function* concat(...iters) {
  for(let iter of iters) {
    for(let value of iter) {
      yield value;
    }
  }
}

/**
 * Build a function that takes the next item from an iterable.
 * That function returns the 'last' value when done.
 * @example
 * const take = taker([ 1, 2 ], 0);
 * const first = take();  // 1
 * const second = take(); // 2
 * const third = take();  // 0
 * const fourth = take(); // 0
 * @param {iterable} iter The iterable to operator on.
 * @param {any} last The value to return if at end.
 * @returns The function that takes the next item.
 */
function taker(iter, last) {
  const it = iter[Symbol.iterator]();
  return function takeNext() {
    const { value, done } = it.next();
    if(done) {
      return last;
    } else {
      return value;
    }
  };
}

/**
 * Fibonacci sequence.
 * @param {number} initial The initial value.
 * @param {number} maximum The maximum value.
 * @returns An iterable fibonacci sequence.
 */
function* fibonacci(initial=1) {
  let previous = 0;
  let current = initial;
  while(1) {
    yield current;
    [ previous, current ] = [ current, previous + current ];
  }
}

/**
 * Map values.
 * @param {iterable} iter The iterable to operator on.
 * @param {function} fn A function to transform/map each item.
 * @returns An iterator of mapped items.
 */
function* map(iter, fn) {
  for(let value of iter) {
    yield fn(value);
  }
}

/**
 * Filter values.
 * @param {iterable} iter The iterable to operator on.
 * @param {function} fn A function to filter each item.
 * @returns An iterator of filtered items.
 */
function* filter(iter, fn) {
  for(let value of iter) {
    if(fn(value)) {
      yield value;
    }
  }
}

/**
 * Slice a range out of an iterable.
 * @param {iterable} iter The iterable to operator on.
 * @param {integer} from The starting index.
 * @param {integer} to The ending index.
 * @returns An iterator of the sliced part.
 */
function* slice(iter, from, to) {
  if(from !== undefined && to === undefined) {
    to = from;
    from = 0;
  }
  if(from === undefined) {
    from = 0;
  }
  if(to === undefined) {
    to = MAX;
  };
  let idx = 0;
  for(let item of iter) {
    if(idx > to) {
      break;
    }
    idx += 1;
    if(idx <= from) {
      continue;
    }
    yield item;
  }
}

/**
 * Advance an iterator by a given amount.
 * @param {iterator} iter The iterator to advance .
 * @param {integer} size The number of items to advance by.
 * @returns The advanced iterator.
 */
function advance(iter, size) {
  return slice(iter, size, MAX);
}

/**
 * Exports.
 */
module.exports = {
  // General.
  taker,
  // Transform the values.
  map,
  // Change the iteration.
  limit,
  filter,
  slice,
  advance,
  concat,
  // Create iterators.
  range,
  repeat,
  fibonacci,
};