Skip to content

danielrbradley/collection-fns

Repository files navigation

Collection Functions

npm version GitHub issues TypeDoc docs Travis Coveralls Dev Dependencies styled with prettier

Really simple functions for working with built-in collection types, inspired by F#'s collection modules design.

Features

  • Modules for iterables, arrays, maps and sets.
  • Composable functional design with full type-safety when used with TypeScript.

Installation

Add package using NPM or yarn

npm i --save collection-fns
yarn add collection-fns

You can import the top level modules directly:

import { Iterables, Arrays, Sets, Maps, pipe } from 'collection-fns'

Additionally, you can import the specific module functions from dist/lib/[module]:

import { groupBy } from 'collection-fns/dist/lib/iterables'

Examples

Calculating primes lazily with iterators:

import { pipe } from 'collection-fns'
import { init, map, filter, count } from 'collection-fns/dist/lib/iterables';

const primes = pipe(
  init({ from: 1, to: 100 }),
  map(x => ({
    x,
    factors: pipe(
      init({ from: 1, to: x }),
      filter(y => x % y === 0)
    )
  })),
  filter(num => count(num.factors) === 2),
  map(num => num.x)
)

for (const prime of primes) {
  console.log(prime)
}

Grouping numbers into odd and even buckets

import { pipe, Maps } from 'collection-fns'
import { init, groupBy } from 'collection-fns/dist/lib/arrays';

const oddAndEven = pipe(
  init({ from: 1, to: 25 }),
  groupBy(i => i % 2 === 0 ? 'even' : 'odd'),
  Maps.ofArray
)

This works by use of partial application, however all functional can also be called directly such as:

Iterables.map(
  Iterables.init({ from: 1, to: 10 }),
  x => x * x))

Pipes

The pipe() function is a stand-in until the pipe (|>) operator gets implemented in ESNext.

For pipes of up to 26 steps, the multi-argument overloads can be used where the first argument is the initial value, and all following arguments are functions take the result of the step before and returning a new result. The result from the final step is then returned as the result of the pipe.

For longer pipes there is an alternative syntax:

  1. The pipe is started by passing pipe(...) a single initial value.
  2. Each .then(...) step in a pipe takes a callback that is passed the value from the previous step.
  3. At the end of the pipe, access the .result property to get the value returned from the last step.

Taking the example from the proposal linked above:

function doubleSay (str) {
  return str + ", " + str;
}
function capitalize (str) {
  return str[0].toUpperCase() + str.substring(1);
}
function exclaim (str) {
  return str + '!';
}

The following statements are equivalent:

let result = exclaim(capitalize(doubleSay("hello")));
result // Prints: "Hello, hello!"

let result = pipe(
  "hello",
  doubleSay,
  capitalize,
  exclaim
)
result // Prints: "Hello, hello!"

let result =
  pipe("hello")
    .then(doubleSay)
    .then(capitalize)
    .then(exclaim)
    .result
result // Prints: "Hello, hello!"

NPM scripts

  • yarn test: Run test suite
  • yarn start: Run yarn build in watch mode
  • yarn test:watch: Run test suite in interactive watch mode
  • yarn test:prod: Run linting and generate coverage
  • yarn build: Generate bundles and typings, create docs
  • yarn lint: Lints code
  • yarn commit: Commit using conventional commit style (husky will tell you to use it if you haven't 😉)