@nkp/iterable
TypeScript icon, indicating that this package has built-in type declarations

1.0.0 • Public • Published

@nkp/iterable

npm version Node.js Package Known Vulnerabilities

Immutable collection utility classes for synchronous and lazy iteration over iterables like arrays, sets, maps and generators.

@nkp/iterable exposes two main utility classes, Collection and LazyCollection.

The Collection class performs faster than the native JavaScript Array class for many methods because it does not check for array holes.

Table of contents

Installation

NPM

npm install @nkp/iterable

Yarn

yarn add @nkp/iterable

Exports

@nkp/iterable targets CommonJS and ES modules. To utilise ES modules consider using a bundler like webpack or rollup.

Array Holes

@nkp/iterable assumes that the iterables fed to them do not have holes.

For consistent behaivour you should not use @nkp/iterable with holey arrays.

Because @nkp/iterable assumes hole-less arrays, the Collection class is faster than the native Array class for a large variety of common operations like filter and map.

Collection types

type Collection LazyCollection
similar to array generator
speed fast slow
memory heavy light

Collection

  • speed: fast
  • memory: heavy

Collection is similar to the native JavaScript Array class.

Like an array, Collection's values exist in memory at all times.

Collection is memory heavy and very fast. Collection is even faster than the native JavaScript Array class in many situations because it doesn't check for holes.

Collection's methods cause instantaneous transformations of it's internal values, as opposed to LazyCollection which only runs transformations when values are requested.

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3, 4]);
let called = false;

collection            // Collection [1, 2, 3, 4]
  .map(n => {
    called = true;
    n + 1;
  })                  // Collection [2, 3, 4, 5]
  .exclude(2)         // Collection [3, 4, 5]
  .pick(3, 4)         // Collection [3, 4]
  .sort(-1);          // Collection [4, 3]

// transformations have executed upon call
called;               // true

collection.toArray(); // Array [4, 3]

LazyCollection

  • speed: slow
  • memory: light

LazyCollection is a lazy stream that's only calculated when values are requested.

LazyCollection does not store its values in memory, only a reference to the initial iterable provided to it.

LazyCollection stores transformations and doesn't execute them until the caller requests data from it.

import { collectLazy } from '@nkp/iterable';

// generator
function * input(): IterableIterator<number> {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
}
const lazy = collectLazy(input);
let called = false;

lazy                // LazyCollection [input]
  .map(n => {
    called = true;
    n + 1;
  })                // LazyCollection [input]
  .exclude(2)       // LazyCollection [input]
  .pick(3, 4)       // LazyCollection [input]
  .sort(-1);        // LazyCollection [input]

// transformations have not executed yet
called;             // false

lazy.toArray();     // Array [4, 3]

// now that data has been requeted, all transformations have run
// and the results have been cached in-case of re-run
called;             // true
lazy;               // LazyCollection [4, 3]

Internally, values are cached 1. when LazyCollection requires all values in memory for a transformation 2. when the values are requested from the callee

const input = [1, 2, 3];
const lazy = new LazyCollection(input)
  .map(n => n + 1)
  .exclude(4)
  .sort();        // LazyCollection [input]

lazy.toArray();   // all transformations run on [1, 2, 3]
lazy.toArray();   // cached results are returned, transformations do not run again
lazy.at(1);       // cached results are returned, transformations do not run again

Usage

Methods

all

Split the collection and operate on it's parts independently then join the results into a tuple (array) and return a single-valued collection with the tuple as it's only value.

Similar to Promise.all, but for collections.

// signature

export interface IHasAll<T> extends Iterable<T> {
  all<M extends Record<PropertyKey, Unary<this, unknown>>>(forks: M): IHasAll<{ [K in keyof M]: ReturnType<M[K]> }>;
  all<R1>(...splits: readonly [Unary<this, R1>]): IHasAll<[R1]>
  all<R1, R2>(...splits: readonly [Unary<this, R1>, Unary<this, R2>]): IHasAll<[R1, R2]>
  all<R1, R2, R3>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>]): IHasAll<[R1, R2, R3]>
  all<R1, R2, R3, R4>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>]): IHasAll<[R1, R2, R3, R4]>
  all<R1, R2, R3, R4, R5>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>, Unary<this, R5>]): IHasAll<[R1, R2, R3, R4, R5]>
  all<R1, R2, R3, R4, R5, R6>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>, Unary<this, R5>, Unary<this, R6>]): IHasAll<[R1, R2, R3, R4, R5, R6]>
  all<R1, R2, R3, R4, R5, R6, R7>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>, Unary<this, R5>, Unary<this, R6>, Unary<this, R7>]): IHasAll<[R1, R2, R3, R4, R5, R6, R7]>
  all<R1, R2, R3, R4, R5, R6, R7, R8>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>, Unary<this, R5>, Unary<this, R6>, Unary<this, R7>, Unary<this, R8>]): IHasAll<[R1, R2, R3, R4, R5, R6, R7, R8]>
  all<R1, R2, R3, R4, R5, R6, R7, R8, R9>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>, Unary<this, R5>, Unary<this, R6>, Unary<this, R7>, Unary<this, R8>, Unary<this, R9>]): IHasAll<[R1, R2, R3, R4, R5, R6, R7, R8, R9]>
  all<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>, Unary<this, R5>, Unary<this, R6>, Unary<this, R7>, Unary<this, R8>, Unary<this, R9>, Unary<this, R10>]): IHasAll<[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10]>
  all<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>, Unary<this, R5>, Unary<this, R6>, Unary<this, R7>, Unary<this, R8>, Unary<this, R9>, Unary<this, R10>, Unary<this, R11>]): IHasAll<[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11]>
  all<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>, Unary<this, R5>, Unary<this, R6>, Unary<this, R7>, Unary<this, R8>, Unary<this, R9>, Unary<this, R10>, Unary<this, R11>, Unary<this, R12>]): IHasAll<[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12]>
  all<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>, Unary<this, R5>, Unary<this, R6>, Unary<this, R7>, Unary<this, R8>, Unary<this, R9>, Unary<this, R10>, Unary<this, R11>, Unary<this, R12>, Unary<this, R13>]): IHasAll<[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13]>
  all<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>, Unary<this, R5>, Unary<this, R6>, Unary<this, R7>, Unary<this, R8>, Unary<this, R9>, Unary<this, R10>, Unary<this, R11>, Unary<this, R12>, Unary<this, R13>, Unary<this, R14>]): IHasAll<[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14]>
  all<R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15>(...splits: readonly [Unary<this, R1>, Unary<this, R2>, Unary<this, R3>, Unary<this, R4>, Unary<this, R5>, Unary<this, R6>, Unary<this, R7>, Unary<this, R8>, Unary<this, R9>, Unary<this, R10>, Unary<this, R11>, Unary<this, R12>, Unary<this, R13>, Unary<this, R14>, Unary<this, R15>]): IHasAll<[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15]>
  all<R>(...splits: readonly (Unary<this, R>)[]): IHasAll<R[]>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect(['index.html', 'script.js', 'style.css']);

collection
  .all(
    (html) => html.matching(/\.html$/),
    (scripts) => scripts.matching(/\.js$/),
    (styles) => styles.matching(/\.css$/),
  )
  /**
   * 
   * Collection [[
   *  Collection ['index.html'],
   *  Collection ['script.js'],
   *  Collection ['style.css']
   * ]]
   */
  .first()
  /**
   * Some [[
   *  Collection ['index.html'],
   *  Collection ['script.js'],
   *  Collection ['style.css']
   * ]]
   */
  .map(([html, scripts, stypes]) => {
    // ...
  })

at

Get the value at a specified index.

Providing a negative index searches the collection from back-to-front.

Similar to Array.prototype.at but returns Maybe<T> instead of T.

// signature

import { Maybe } from '@nkp/maybe';

interface IHasAt<T> extends Iterable<T> {
  at(index: number): Maybe<T>;
}
// usage

import { collect } from '@nkp/iterable';

collect([1, 2]).at(0);    // Some [1]
collect([1, 2]).at(-1);   // Some [-2]
collect([1, 2]).at(2);    // None
collect([1, 2]).at(-3);   // None

btw

Keep values between the two limits.

// signature

import { Betweenable } from '@nkp/iterable';

interface IHasBtw<T> extends Iterable<T> {
  btw(left: Betweenable, right: Betweenable): IHasBtw<T>;
}
// usage

import { collect } from '@nkp/iterable';

// numbers
const numbers = collect([1, 2, 3, 4, 5]);

numbers.btw(2, 4);                               // Collection [2, 3, 4]
numbers.btw([2, true], 4);                       // Collection [2, 3, 4]
numbers.btw([2, false], 4);                      // Collection [   3, 4]
numbers.btw(2, [4, true]);                       // Collection [2, 3, 4]
numbers.btw(2, [4, false]);                      // Collection [2  3   ]
numbers.btw({ value: 2, inclusive: true }, 4);   // Collection [2, 3, 4]
numbers.btw({ value: 2, inclusive: false }, 4);  // Collection [   3, 4]
numbers.btw(2, { value: 4, inclusive: true });   // Collection [2, 3, 4]
numbers.btw(2, { value: 4, inclusive: false });  // Collection [2, 3   ]

// dates
const dates = collect([
  new Date('1990-01-01'),
  new Date('2000-01-01'),
  new Date('2010-01-01'),
]);

dates.btw(
  new Date('1995-01-01'),
  new Date('2005-01-01'),
); // Collection [Date('2000-01-01')]
//

compact

Remove falsy values from the collection.

// signature

interface IHasCompact<T> extends Iterable<T> {
  compact(): IHasCompact<NonNullable<T>>;
}
// usage

import { collect } from '@nkp/iterable';

const collection: Collection<number> = collect([1, null, undefined, false, 0, 3]);

collection.notNullable(); // Collection [1, 3]

concat

Concatenate an iterable onto the end of the collection.

Unlike Array.protototype.concat, IHasConcat.concat only accepts a single array argument and does not accept variadic arguments. For spread arguments use push;

// signature

import { Iterateable } from '@nkp/iterable';

interface IHasConcat<T> extends Iterable<T> {
  concat(concat: Iterateable<T>): IHasConcat<T>;
}
// usage

import { collect } from '@nkp/iterable';

collect([1, 2]).concat([3, 4]); // Collection [1, 2, 3, 4]

every

Returns true if the given callback returns truthy for every value in the collection.

Similar to Array.prototype.every and sibling of some.

// signature

interface IHasEvery<T> extends Iterable<T> {
  every(callbackfn: ((value: T, currentIndex: number) => boolean)): boolean;
}
// usage

import { collect } from '@nkp/iterable';

collect([1, 2]).every(Boolean);     // true
collect([0, 1]).every(Boolean);     // false - 0 is falsy
collect([0, 1]).every(n => n >= 0); // true

exclude

Removes values from the collection that equal any of the given values.

// signature

interface IHasExclude<T> extends Iterable<T> {
  exclude(...remove: readonly T[]): IHasExclude<T>;
}
// usage

import { collect } from '@nkp/iterable';

collect([1, 2, 3]).exclude(1, 2); // Collection [3]

filter

Removes values from the collection if their callback returns falsy.

Similar to Array.prototype.filter.

// signature

interface IHasFilter<T> extends Iterable<T> {
  filter<U extends T>(callbackfn: ((value: T, currentIndex: number) => value is U)): IHasFilter<U>;
  filter(callbackfn: (value: T, currentIndex: number) => boolean): IHasFilter<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.filter(n => n > 1); // Collection [2, 3]

find

Find a value in the collection.

Similar to Array.prototype.find but returns Maybe<T> instead of T | undefined.

// signature

import { Maybe } from '@nkp/maybe';

interface IHasFind<T> extends Iterable<T> {
  find(callbackfn: (value: T, currentIndex: number) => boolean): Maybe<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.find(3); // Some [3]
collection.find(4); // None

findIndex

Find the index of a value in the collection.

Similar to Array.prototype.findIndex but returns Maybe<number> instead of number.

// signature

import { Maybe } from '@nkp/maybe';

interface IHasFindIndex<T> extends Iterable<T> {
  findIndex(callbackfn: (value: T, currentIndex: number) => boolean): Maybe<number>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.findIndex(3); // Some [2]
collection.findIndex(4); // None

first

Get the first value from the collection.

Returns Some<T> if the value exists, otherwise returns None.

// signature

import { Maybe } from '@nkp/maybe';

interface IHasFirst<T> extends Iterable<T> {
  first(): Maybe<T>;
}
// usage

import { collect } from '@nkp/iterable';

const someCollection = collect([1, 2, 3]);
someCollection.first(); // Some [1]

const noneCollection = collect([]);
noneCollection.first(); // None

flat

Flattens a nested collection where the nested values may by any iterable.

Similar to Array.prototype.flat.

// signature

interface IHasFlat<T> extends Iterable<T> {
  flat<U>(this: IHasFlat<Iterable<U>>): IHasFlat<U>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);

collection.flat(); // Collection [1, 2, 3, 4, 5, 6, 7, 8, 9]

flatMap

Map the collection's values to iterables and flatten them into single collection.

Similar to Array.prototype.flatMap.

// signature

import { Iterateable } from '@nkp/iterable';

interface IHasFlatMap<T> extends Iterable<T> {
  flatMap<U>(callbackfn: (value: T, currentIndex: number) => Iterateable<U>): IHasFlatMap<U>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.map(collect); // Collection [Collection [1], Collection [2], Collection [3]]

collection.flatMap(collect); // Collection [1, 2, 3]

collection.map(collect).flat(); // Collection [1, 2, 3]

flatSome

Flatten Some values and filter out None's from the collection.

The collection must be of type Maybe.

// signature

import { Some, None, Maybe } from '@nkp/iterable';

interface IHasFlatSome<T> extends Iterable<T> {
  flatSome<U>(this: IHasFlatSome<Some<U>>): IHasFlatSome<U>;
  flatSome<U>(this: IHasFlatSome<Maybe<U>>): IHasFlatSome<U>;
  flatSome(this: IHasFlatSome<None>): IHasFlatSome<never>;
}
// usage

import { Maybe } from '@nkp/maybe';
import { collect } from '@nkp/iterable';

const collection = collect([Maybe.some(1), Maybe.none, Maybe.some(3)]);

collection.flatSome(); // Collection [1, 3]

forEach

Execute a callback for each value in the collection.

Similar to Array.prototype.forEach.

// signature

interface IHasForEach<T> extends Iterable<T> {
  forEach(callbackfn: ((value: T, index: number) => unknown)): void;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.forEach(n => console.log(n)); // 1, 2, 3

forkFlat

Fork the collection to split and operate on it's parts independently then flatten the results together.

// signature

import { Iterateable } from '@nkp/iterable';

interface IHasForkFlat<T> extends Iterable<T> {
  forkFlat<R>(...forks: readonly (Unary<this, Iterateable<R>>)[]): IHasForkFlat<R>;
}
// usage

import { path } from 'path';
import { collect } from '@nkp/iterable';

const collection = collect(['index.html', 'script.js', 'style.css']);

collection.forkFlat(
  (html) => html.matching(/\.html$/).map(file => path.join('public', file)),
  (scripts) => scripts.matching(/\.js$/).map(file => path.join('public', 'scripts', file)),
  (styles) => styles.matching(/\.css$/).map(file => path.join('public', 'styles', file)),
) 
  // Collection [
  //  'public/index.html',
  //  'public/scripts/script.js',
  //  'public/styles/style.css'
  // ]

getSize

Get the size of the collection.

Similar to Array.prototype.length.

// signature

interface IHasGetSize<T> extends Iterable<T> {
  getSize(): number;
}
import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.getSize(); // 3;

gt

Keep values greater-than the given value.

// signature

import { Orderable } from '@nkp/iterable';

interface IHasGt<T> extends Iterable<T> {
  gt(value: Orderable): IHasGt<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.gt(2); // Collection [3]

gte

Keep values greater-than or equal-to the given value.

// signature

import { Orderable } from '@nkp/iterable';

interface IHasGte<T> extends Iterable<T> {
  gte(value: Orderable): IHasGte<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.gte(2); // Collection [2, 3]

indexOf

Get the index of the value in the collection.

// signature

import { Maybe } from '@nkp/maybe';

interface IHasIndexOf<T> extends Iterable<T> {
  indexOf(value: T): Maybe<number>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.indexOf(2); // Some [1]
collection.indexOf(4); // None

join

Stringify and join collection values with a separator.

Similar to Array.prototype.join.

// signature

interface IHasJoin<T> extends Iterable<T> {
  join(separator?: string): string;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect(['hello', 'world']);

collection.join(' '); //  'hello world'

lt

Keep values less-than the given value.

// signature

import { Orderable } from '@nkp/iterable';

interface IHasLt<T> extends Iterable<T> {
  lt(value: Orderable): IHasLt<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.lt(2); // Collection [1]

lte

Keep values less-than or equal-to the given value.

// signature

import { Orderable } from '@nkp/iterable';

interface IHasLte<T> extends Iterable<T> {
  lte(value: Orderable): IHasLte<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.lte(2); // Collection [1, 2]

map

Map values in the collection.

Similar to Array.prototype.map.

// signature

interface IHasMap<T> extends Iterable<T> {
  map<U>(callbackfn: ((value: T, index: number) => U)): IHasMap<U>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.map(n => n + 1); // Collection [1, 2, 3]

mapSelf

Map the collection instance itself.

// signature

interface IHasMapSelf<T> extends Iterable<T> {
  mapSelf<U>(callbackfn: ((self: this) => U)): U;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.mapSelf(self => Array.from(self)[0]); // 1

mapSome

Map into a Maybe and filter and flatten the value back into a collection.

// signature

import { Maybe, } from '@nkp/iterable';

interface IHasMapSome<T> extends Iterable<T> {
  mapSome<U>(callbackfn: (value: T, currentIndex: number) => Maybe<U>): IHasMapSome<U>;
}
// usage

import { Maybe } from '@nkp/maybe';
import { collect } from '@nkp/iterable';

const collection = collect([1, 3]);

collection.mapSome(
  (n) => n <= 1 ? Maybe.some(n) : Maybe.none);
// Collection [1]

match

Match items against the regex.

Returns Some if the match succeded and None if not.

// signature

import { Maybe } from '@nkp-maybe';

interface IHasMatch<T> extends IHasForEach<T> {
  match(regexp: string | RegExp): IHasMatch<Maybe<RegExpMatchArray>>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect(['index.html', 'style.css', 'script.js']);

collection.match(/\.(css|js)$/);
/**
 * Collection [
 *   None
 *   Some [[RegExpMatchArray] {
 *     0: '.css',
 *     1: 'css',
 *     index: 5,
 *     input: 'style.css',
 *     groups: undefined,
 *   }]
 *   Some [[RegExpMatchArray] {
 *     0: '.js',
 *     1: 'js',
 *     index: 5,
 *     input: 'style.css',
 *     groups: undefined,
 *   }]
 * ]
 */

matchFlat

Match items against the regex.

Only keeps successful matches.

// signature

interface IHasMatchFlat<T> extends Iterable<T> {
  matchFlat(regexp: string | RegExp): IHasMatch<RegExpMatchArray>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect(['index.html', 'style.css', 'script.js']);

collection.matchFlat(/\.(css|js)$/);
/**
 * Collection [
 *   [RegExpMatchArray] {
 *     0: '.css',
 *     1: 'css',
 *     index: 5,
 *     input: 'style.css',
 *     groups: undefined,
 *   }
 *   [RegExpMatchArray] {
 *     0: '.js',
 *     1: 'js',
 *     index: 5,
 *     input: 'style.css',
 *     groups: undefined,
 *   }
 * ]
 */

matching

Keeps values matching the given regex.

// signature

interface IHasMatching<T> extends IHasForEach<T> {
  matching(regexp: RegExp | string): IHasMatching<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect(['index.html', 'style.css', 'script.js']);

collection.matching(/\.css$/); // Collection ['style.css']

notMatching

Removes values matching the given regex from the collection.

// signature

interface IHasNotMatching<T> extends Iterable<T> {
  notMatching(regexp: RegExp | string): IHasNotMatching<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect(['index.html', 'style.css', 'script.js']);

collection.notMatching(/\.css$/); // Collection ['index.html', 'script.js']

notNull

Remove null values from the collection.

// signature

interface IHasNotNull<T> extends Iterable<T> {
  notNull(): IHasNotNull<T extends null ? never : T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, null, 3]);

collection.notNull(); // Collection [1, 3]

notNullable

Remove null and undefined values from the collection.

// signature

interface IHasNotNullable<T> extends Iterable<T> {
  notNullable(): IHasNotNullable<NonNullable<T>>;
}
// usage

import { collect } from '@nkp/iterable';

const collection: Collection<number> = collect([1, null, undefined, 3]);

collection.notNullable(); // Collection [1, 3]

notUndefined

Removes undefined values from the collection.

// signature

interface IHasNotUndefined<T> extends Iterable<T> {
  notUndefined(): IHasNotUndefined<T extends undefined ? never : T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, undefined, 3]);

collection.notUndefined(); // Collection [1, 3]

partition

Partition the collection grouping by callback return value

// signature

interface IHasPartition<T> extends Iterable<T> {
  partition<R>(callbackfn: ((
    value: T,
    index: number
  ) => R)): IHasPartition<IHasPartition<T>>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([
  'contact/index.html',
  'contact/script.js',
  'contact/style.css',
  'about/index.html',
  'about/script.js',
  'about/style.css',
]);

collection
  .partition(
    // fork on the directory name
    (html) => html.match(/(.*)[\\/][^\\/\.]*\.html$/).first().value,
    (scripts) => scripts.match(/(.*)[\\/][^\\/\.]*\.js$/).first().value,
    (styles) => styles.match(/(.*)[\\/][^\\/\.]*\.css$/).first().value,
  )
  /**
   * 
   * Collection [
   *  Collection [
   *    'contact/index.html'
   *    'contact/script.js'
   *    'contact/style.css'
   *  ],
   *  Collection [
   *    'about/index.html'
   *    'about/script.js'
   *    'about/style.css'
   *  ],
   * ]
   */

pick

Keep values in the collection that equal any of the given values.

// signature

interface IHasPick<T> extends Iterable<T> {
  pick(...keep: readonly T[]): IHasPick<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.pick(1, 2); // Collection [1, 2]

pluck

Pluck the key from each value in the collection.

// signature

interface IHasPluck<T> extends IHasForEach<T> {
  pluck<K extends keyof T>(key: K): IHasPluck<T[K]>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collection([
  { prop: 'a' },
  { prop: 'b' },
  { prop: 'c' },
]);

collection.pluck('prop'); // Collection ['a', 'b', 'c']

precat

Concatenate an iterable onto the start of the collection.

// signature

import { Iterateable } from '@nkp/iterable';

interface IHasPrecat<T> extends Iterable<T> {
  precat(precat: Iterateable<T>): IHasPrecat<T>;
}
// usage

import { collect } from '@nkp/iterable';

collect([1, 2]).precat([3, 4]); // Collection [3, 4, 1, 2]

push

Push values onto the end of the collection.

Similar to Array.prototype.push but returns the new collection instead of the number of arguments pushed.

// signature

interface IHasPush<T> extends Iterable<T> {
  push(...pushed: T[]): IHasPush<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.push(4, 5); // Collection [1, 2, 3, 4, 5]

reduce

Reduce the collection to a single value.

Similar to Array.prototype.reduce.

// signature

interface IHasReduce<T> extends Iterable<T> {
  reduce<U>(
    callbackfn: ((
      previousValue: T,
      currentValue: U,
      currentIndex: number
    ) => U),
    initial: U,
  ): U;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

// sum the collection from left-to-right
collection.reduce((next, acc) => acc + next, 0); // 6

reduceRight

Reduce the collection to a single value, from right to left.

Similar to Array.prototype.reduceRight.

// signature

interface IHasReduceRight<T> extends Iterable<T> {
  reduceRight<U>(
    callbackfn: ((
      previousValue: T,
      currentValue: U,
      currentIndex: number
    ) => U),
    initial: U,
  ): U;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

// stringify and concatenate the collection values from right-to-left
collection.reduceRight((next, acc) => acc + String(next), ''); // '321'

reverse

Reverse the ordering of values in the collection.

Similar to Array.prototype.reverse but does not mutate the callee.

// signature

interface IHasReverse<T> extends Iterable<T> {
  reverse(): IHasReverse<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.reverse(); // Collection [3, 2, 1]

// not mutated
collection;           // Collection [1, 2, 3]

skip

Removes the first n values from the collection.

// signature

interface IHasSkip<T> extends Iterable<T> {
  skip(count?: number): IHasSkip<T>;
}
// usage

import { collect } from '@nkp/iterable';

collect([1, 2, 3]).skip(2); // Collection [3]

slice

Slice elements from the collection from start to end.

Similar to Array.prototype.slice.

// signature

interface IHasSlice<T> extends Iterable<T> {
  slice(start?: number, end?: number): IHasSlice<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([0, 1, 2, 3, 4]);

// from index 2, ending before index 4
collection.slice(2, 4); // Collection [2, 3]

some

Returns true if the callback returns truthy for any value in the collection.

Similar to Array.prototype.some. Sibling of every.

// signature

interface IHasSome<T> extends Iterable<T> {
  some(callbackfn: (value: T, currentIndex: number) => boolean): boolean;
}
// usage

import { collect } from '@nkp/iterable';

collect([1, 2]).some(Boolean); // true
collect([0, 1]).some(Boolean); // true
collect([0, 0]).some(Boolean); // false
collect([0, false]).some(Boolean); // false

sort

Sorts values in the collection with sensible defaults.

Similar to Array.prototype.sort but sorts numerically by default instead of alphabetically and does not mutate the callee.

// signature

import { SortDirection } from '@nkp/sort';

interface IHasSort<T> extends Iterable<T> {
  sort(sort: SortDirection<T>): IHasSort<T>;
}

/**
 * type SortDirection<T> =
 *   | 'asc' | 'ASC'
 *   | 'desc' | 'DESC'
 *   | 1 | '1'
 *   | -1 | '-1'
 *   | ((a: T, b: T) => number)
 */
// usage

import { collect } from '@nkp/iterable';

// numeric only
const numeric =   collect([1, 4, BigInt(3), 2]);
// ascending
numeric.sort(1);  // Collection [1, 2, BigInt(3), 4]
// descending
numeric.sort(-1); // Collection [4, BigInt(3), 2, 1]

// alphabetical only - sorts by char code
const alpha =     collect(['a', 'c', 'B', 'd']);
// ascending
alpha.sort(1);    // Collection ['B', 'a', 'c', 'd']
// descending
alpha.sort(-1);   // Collection ['d', 'c', 'a', 'B']

// alphabetic and numeric
// sorts numerically then alphabetically
const alpha =     collect([1, 'a', 3, 'c', 2, 'b']);
// ascending
alpha.sort(1);    // Collection [1, 2, 3, 'a', 'b', 'c']
// desecending
alpha.sort(-1);   // Collection ['c', 'b', 'a', 3, 2, 1]

take

Keep the first n values in the collection.

// signature

interface IHasTake<T> extends Iterable<T> {
  take(count?: number): IHasTake<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.take(2); // Collection [1, 2]

tap

Execute a callback for each value in the collection without changing the collection.

Useful for logging.

Similar to Array.prototype.forEach but returns the collection.

// signature

interface IHasTap<T> extends Iterable<T> {
  tap(callbackfn: ((value: T, index: number) => unknown)): this;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection
  .tap((value, i) => console.log(`${i}: ${value}`));
  // 0: 1
  // 1: 2
  // 2: 3
  .map((value) => {
    // ...
  });

tapSelf

Execute a callback with a reference to the collection itself.

Returns the original collection.

Useful for logging.

// signature

interface IHasTapSelf<T> extends Iterable<T> {
  tapSelf(callbackfn: ((self: IHasTapSelf<T>) => unknown)): this;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection
  .map((value) => value + 1)
  .tapSelf((c) => console.log(`Right now the collection is: ${c.toString()}`))
  .map((value) => {
    // ...
  });

toArray

Transform the collection into n array.

// signature

interface IHasToArray<T> extends Iterable<T> {
  toArray(): Array<T>
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.toArray(); // Array [1, 2, 3]

toSet

Transform the collection into an ES6 set.

// signature

interface IHasToSet<T> extends Iterable<T> {
  toSet(): Set<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3, 3]);

collection.toSet(); // Set [1, 2, 3]

toMap

Transform the collection into an ES6 map.

// signature

interface IHasToMap<T> extends Iterable<T> {
  toMap<K, V>(this: IHasToMap<[K, V]>): Map<K, V>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect<[number, number]>([[1, 1], [2, 2], [3, 3]]);

collection.toMap(); // Map [[1, 1], [2, 2], [3, 3]]

unique

Remove duplicate values from the collection.

// signature

interface IHasUnique<T> extends Iterable<T> {
  unique(): IHasUnique<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 2]);

collection.unique(); // Collection [1, 2]

unshift

Shift values onto the front of the collection.

Similar to Array.prototype.shift.

// signature

interface IHasUnshift<T> extends Iterable<T> {
  unshift(...unshifted: readonly T[]): IHasUnshift<T>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.shift(-1, -2, -3); // Collection [-1, -2, -3, 1, 2, 3]

zip

Join the collection with an iterable, by index.

The resulting collection terminates with the first input iterable.

// signature

import { Iterateable } from '@nkp/iterable';

interface IHasZip<T> extends Iterable<T> {
  zip<U>(right: Iterateable<U>): IHasZipShort<[T, U]>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.zip([-1, -2, -3, -4]);
// Collection<[
//  [1, -1,],
//  [2, -2,],
//  [3, -3,],
// ]>

zipLong

Join the collection with an iterable, by index.

The resulting collection terminates with the final input iterable.

// signature

import { Iterateable } from '@nkp/iterateable';
import { Maybe } from '@nkp/maybe';

interface IHasZipLong<T> extends Iterable<T> {
  zipLong<U>(right: Iterateable<U>): IHasZipLong<[Maybe<T>, Maybe<U>]>;
}
// usage

import { collect } from '@nkp/iterable';

const collection = collect([1, 2, 3]);

collection.zipLong([-1, -2, -3, -4]);
// Collection<[
//  [Some<1>, Some<-1>,],
//  [Some<2>, Some<-2>,],
//  [Some<3>, Some<-3>,],
//  [None, Some<-4>,],
// ]>

Publishing a new version

To a release a new version:

  1. Update the version number in package.json
  2. Push the new version to the master branch on GitHub
  3. Create a new release on GitHub for the latest version

This will trigger a GitHub action that tests and publishes the npm package.

Readme

Keywords

Package Sidebar

Install

npm i @nkp/iterable

Weekly Downloads

0

Version

1.0.0

License

Mozilla Public License Version 2.0

Unpacked Size

256 kB

Total Files

19

Last publish

Collaborators

  • nickkelly