Skip to content

Latest commit

 

History

History
990 lines (778 loc) · 28 KB

README.md

File metadata and controls

990 lines (778 loc) · 28 KB

What is declarative-js

declarative-js is modern JavaScript library, that helps to:

  • tackle array transformation with built in JavaScript array api (e.g. array.filter(toBe.unique())),
  • provide a type-level solution for representing optional values instead of null references.

Why declarative-js?

  • performance (link to benchmarks)
  • ability to use with built in api (js array)
  • it is written in typescript. All functions provides great type inference
  • declarative code instead of imperative
  • reduces boilerplate code providing performant and tested solutions
  • comprehensive documentation (link)
  • Target version of javascript is ES5
  • Internet Explorer 11 compatible

npm version Build Status Coverage Status Language grade: JavaScript

Install

npm i declarative-js --save

Array Functions

Reducers

API documentation link

toObject

Collects items by key, to object. Second parameter in function toObject can be used to resolve value to put in it. If it is omitted, whole object will be put as a value. As a second parameter Reducers.ImmutableObject() can be passed instead of just {}. If function resolves key, that already exists it will throw an Error

performance benchmark: link

Reduce to object by key callback

import { Reducers } from 'declarative-js'
import toObject = Reducers.toObject

const data = [{name: 'john', age: 11}, {name: 'mike',  age: 12}]

data.reduce(toObject(person => person.name), {})
// {
//   john: {name: 'john', age: 11},
//   mike: {name: 'mike',  age: 12}
// }

Reduce to object by key callback, resolves value by second parameter as a a callback function

import ImmutableObject = Reducers.ImmutableObject

const data = [{name: 'john', age: 11}, {name: 'mike',  age: 12}]

data.reduce(toObject(person => person.name, person => person.age), ImmutableObject())
// {
//   john: 11,
//   mike: 12
// }

Reduce to object, keys are resolve by first callback, value is resolve by second callback. In case the resolved key already exists in object third callback as will merge values.

const data = [
    { title: 'Predator', genre: 'scy-fy' },
    { title: 'Predator 2', genre: 'scy-fy'},
    { title: 'Alien vs Predator', genre: 'scy-fy' }, 
    { title: 'Tom & Jerry', genre: 'cartoon' }, 
]
data.reduce(toObject(
        movie => movie.genre, 
        movie => [movie.title], 
        (movie1, movie2) => movie1.concat(movie2)), 
        {}
    )
// {    
// 'scy-fy': ['Predator', 'Predator 2', 'Alien vs Predator'],
//  'cartoon': ['Tom & Jerry']
// }    

groupBy

Groups by key resolved from callback to map where key is string and value is an array of items. (groupby for javascript) Custom implementation of Map can be passed as a second parameter. It must implement interface MethodMap. Provided implementations can be imported from same namespace Reducer.ImmutableMap or Reducer.Map

group by original values example

performance benchmark: link

import { Reducers } from 'declarative-js'
import groupBy = Reducers.groupBy
import Map = Reducers.Map

const data = [
 { title: 'Predator', genre: 'sci-fi' },
 { title: 'Predator 2', genre: 'sci-fi'},
 { title: 'Alien vs Predator', genre: 'sci-fi' }, 
 { title: 'Tom & Jerry', genre: 'cartoon' } 
]

data.reduce(groupBy(movie => move.genre), Map())
data.reduce(groupBy('genre'), Map())

// {
//   'scy-fy': [
//    { title: 'Predator', genre: 'scy-fy' },
//    { title: 'Predator 2', genre: 'scy-fy' },
//    { title: 'Alien vs Predator', genre: 'scy-fy' }
//   ],
//   'cartoon': [
//    { title: 'Tom & Jerry', genre: 'cartoon' }
//   ],
// }

group by transformed values example

import { Reducers } from 'declarative-js'
import groupBy = Reducers.groupBy
import Map = Reducers.Map

const data = [
 { title: 'Predator', genre: 'sci-fi' },
 { title: 'Predator 2', genre: 'sci-fi'},
 { title: 'Alien vs Predator', genre: 'sci-fi' }, 
 { title: 'Tom & Jerry', genre: 'cartoon' } 
]

data.reduce(
    groupBy(
        movie => move.genre, 
        movie => movie.title
    ), 
    Map(),
)
data.reduce(groupBy('genre', movie => movie.title), Map())

// {
//   'scy-fy': [
//     'Predator',
//     'Predator2',
//     'Alien vs Predator'
//   ],
//   'cartoon': [ 'Tom & Jerry' ],
// }

flat

Flats 2d array to array

import { Reducers } from 'declarative-js'
import flat = Reducers.flat

[[1, 2], [2, 3], [3, 4]].reduce(flat, []) // [1, 2, 2, 3, 3, 4]

zip

Collects two arrays into one array of tuples, two element array([x ,y]). The length of zipped array will be length of shortest array.

performance benchmark: link

import { Reducers } from 'declarative-js'
import zip = Reducers.zip

// array lengths are equal
let a1 = [1, 2, 3]
let a2 = ['x', 'y', 'z']
let zippedA = a1.reduce(zip(a2), [])
// [[1, 'x'], [2, 'y'], [3, 'z']]

// origin array is longer
let b1 = [1, 2, 3, 4]
let b2 = ['x', 'y', 'z']
let zippedB = b1.reduce(zip(b2), [])
// [[1, 'x'], [2, 'y'], [3, 'z']]

// zip array is longer
let c1 = [1, 2, 3]
let c2 = ['x', 'y', 'z', 'extra']
let zippedC = c1.reduce(zip(c2), [])
// [[1, 'x'], [2, 'y'], [3, 'z']]

Can pass a function as a second parameter, that will combine two elements

import { Reducers } from 'declarative-js'
import zip = Reducers.zip

let numbers = [1, 2, 3]
let letters = ['x', 'y', 'z']
let toObject = (number, letter) => ({number, letter})
let zippedA = numbers.reduce(zip(letters, toObject), [])
// [
//    {number: 1, letter: 'x'}, 
//    {number: 2, letter: 'y'}, 
//    {number: 3, letter: 'z'}
// ]

zipAll

Collects all arrays to arrays of arrays, with elements at being grouped with elements from other arrays by same index. The length of zipped array will be length of shortest array. Almost the same as Reducer.zip, except zipAll accepts multiple array to zip with.

import { Reducer } from 'declarative-js'
import zipAll = Reducer.zipAll

let numbers = [1, 2]
let chars = ['a', 'b']
let booleans = [true, false]
let result = numbers.reduce(zipAll(chars, booleans), [])
// [[1, 'a', true], [2, 'b', false]]

let numbers1 = [1, 2, 3, 4, 5]
let chars1 = ['a', 'b']
let booleans1 = [true, false]
let result1 = numbers.reduce(zipAll(chars, booleans), [])
// [[1, 'a', true], [2, 'b', false]]

unzip

It does the opposite as Reducer.zip or Reducer.zipAll. It collects from all zipped arrays one arrays, that was before zip. Takes from each nested arrays and element and for each index will collect to new array. The length of and array will be the shortest length of arrays to unzip

import { Reducer } from 'declarative-js'
import zipAll = Reducer.zipAll
import unzip = Reducer.unzip

let zipped = [[1, 'a', true], [2, 'b', false]]
zipped.reduce(unzip(), [])
// [
//   [1, 2],
//   ['a', 'b'],
//   [true, false]
// ]

let zippedDifferentLength = [[1, 'a'], [2, 'b', false]]
zippedDifferentLength.reduce(unzip(), [])
// [
//   [1, 2],
//   ['a', 'b']
// ]

let numbers = [1, 2]
let chars = ['a', 'b']
let booleans = [true, false]
let zipped = numbers.reduce(zipAll(chars, booleans), [])
zipped.reduce(unzip(), [])
// matches content of
// [
//   numbers, 
//   chars,
//   booleans
// ]

partitionBy

It reduces array in a tuple ([[], []]) with two arrays. First array contains elements, that matches predicate, second array, that does not match. As a second parameter in reduce (callback, initialValue), as an initial value need to pass empty tuple of arrays ([[], []]) Or use Reducer.Partition function to create initial value for it.

Predicate is :

  • an object, which key and values must match current element. For matching all key-value pairs, element will be placed in first partition array.
  • objects key, that will be coerced to boolean with Boolean constructor (Boolean())
  • a function that takes current element as a parameter and returns boolean

Example predicate function

performance benchmark: link

import { Reducer } from 'declarative-js'
import partitionBy = Reducer.partitionBy
import Partition = Reducer.Partition
 
let array = [1, 2, 3, 4, 5, 6]
let isEven = number => number % 2 === 0
array.reduce(partitionBy(isEven), [[], []])
// [[2, 4, 6], [1, 3, 5]]

let array = [1, 2, 3, 4, 5, 6]
let isEven = number => number % 2 === 0
array.reduce(partitionBy(isEven), Partition())
// [[2, 4, 6], [1, 3, 5]]

Example element key

performance benchmark: link

import { Reducer } from 'declarative-js'
import partitionBy = Reducer.partitionBy
import Partition = Reducer.Partition
 
let array = [
    { value: 1, isEven: false },
    { value: 2, isEven: true }, 
    { value: 3, isEven: false }
  ]
array.reduce(partitionBy('isEven'), [[], []])
// [
//   [{ value: 2, isEven: true }],
//   [{ value: 1, isEven: false }, { value: 3, isEven: false }]
// ]

array.reduce(partitionBy('isEven'), Partition())
// [
//   [{ value: 2, isEven: true }],
//   [{ value: 1, isEven: false }, { value: 3, isEven: false }]
// ]

Example object to match

performance benchmark: link

import { Reducer } from 'declarative-js'
import partitionBy = Reducer.partitionBy
import Partition = Reducer.Partition

 let array = [
     { name: 'Bart', lastName: 'Simpson' },
     { name: 'Homer', lastName: 'Simpson' },
     { name: 'Ned', lastName: 'Flanders' },
 ]
array.reduce(partitionBy({ lastName: 'Simpson' }), [[], []])
// [
//   [{ name: 'Bart', lastName: 'Simpson' }, { name: 'Homer', lastName: 'Simpson' } ],
//   [{ name: 'Ned', lastName: 'Flanders' }]
// ]

array.reduce(partitionBy({ lastName: 'Simpson' }), Partition())
// [
//   [{ name: 'Bart', lastName: 'Simpson' }, { name: 'Homer', lastName: 'Simpson' } ],
//   [{ name: 'Ned', lastName: 'Flanders' }]
// ]

toMap

Collects items by key, to map. Second parameter in function toMap can be used to resolve value to put in map. If it is omitted, whole object will be put as a value to map. Custom implementation of Map can be passed as a second parameter. It must implement interface MethodMap. Provided implementations can be imported from same namespace Reducer.ImmutableMap or Reducer.Map If function resolves key, that already exists it will throw an Error

performance benchmark: link

import { Reducers } from 'declarative-js'
import toMap = Reducers.toMap
import Map = Reducers.Map

const data = [{name: 'john', age: 11}, {name: 'mike',  age: 12}]

const reduced1 = data.reduce(toMap(va => va.name), Map())
reduced1.keys() // ['john', 'mike']
reduced1.values() // [{name: 'john', age: 11}, {name: 'mike', age: 12}]

const reduced2 = data.reduce(toMap(va => va.name, va => va.age), Map())
reduced2.keys() // ['john', 'mike']
reduced2.values() // [11, 12]

toMergedObject

Reduces array of objects to one object There is three predefined merge strategies

import { Reducer } from 'declarative-js'
/**
 * Overrides value by duplicated key while merging objects
 */
Reducer.MergeStrategy.OVERRIDE
/**
 * Keys in objects must be unique
 */
Reducer.MergeStrategy.UNIQUE
/**
 * Keys in objects may have duplicates, but values in these key must be equal
 */
Reducer.MergeStrategy.CHECKED

Default strategy is OVERRIDE.

import { Reducers } from 'declarative-js'
import toMergedObject = Reducers.toMergedObject
import MergeStrategy = Reducers.MergeStrategy

[ {e: 1}, {d: 2}, {c: 3} ].reduce(toMergedObject(), {}) // {e: 1, d: 2, c: 3}

// values by duplicated keys can be equal
[ {e: 1}, {e: 1}, {c: 3} ].reduce(toMergedObject(MergeStrategy.CHECKED), {}) // {e: 1, c: 3}

[ {e: 1}, {e: 1}, {c: 3} ].reduce(toMergedObject(MergeStrategy.UNIQUE), {}) // ERROR
[ {e: 1}, {e: 2}, {c: 3} ].reduce(toMergedObject(MergeStrategy.UNIQUE), {}) // ERROR

Since MergeStrategy is just a predicate function with declaration: (aggregatorValue: T, currentValue: T, key: string) => boolean Developer can define its own predicate to avoid object traversing and check, are all properties equal.

import { Reducers } from 'declarative-js'
import toMergedObject = Reducers.toMergedObject

[ 
    { 
        predator: {
            title: 'Predator', 
            genre: 'scy-fy'
        }
    }, 
    {
        predator: {
            title: 'Predator', 
            genre: 'scy-fy'
        }
    },
    {
        alienVsPredator: {
            title: 'Alien vs Predator', 
            genre: 'scy-fy'
        }
    }
]
// merge objects if properties 'title' are not equal, otherwise throw error
// if there is not need to throw an error, default merge strategy will 
// return always true and will override a property.
.reduce(toMergedObject((o1, o2) => o1.title !== o2.title), {}) // ERROR

pairwise

Groups pairs of consecutive elements together and returns them as an array of two values.

import { Reducers } from 'declarative-js'
import pairwise = Reducers.pairwise

const array = [1, 2, 3]
array.reduce(pairwise(), []) // [[1, 2], [2, 3]]

scan

Groups pairs of consecutive elements together and returns them as an array of two values. Applies an accumulator function over the current element and returns each intermediate result for accumulation

import { Reducers } from 'declarative-js'
import scan = Reducers.scan

const numbers = [1, 2, 3]
numbers.reduce(scan((acc, value) => acc + value, 0), [])
// [1, 3, 6]

const strings = ['a', 'b', 'c']
strings.reduce(scan((acc, value) => acc.concat(value), ''), [])
// ['a', 'ab', 'abc']

min

Finds min value of an array of numbers

import { Reducers } from 'declarative-js'
import min = Reducers.min

[1, 2, 3].reduce(min)) // 1

max

Finds min value of an array of numbers

import { Reducers } from 'declarative-js'
import max = Reducers.max

[1, 2, 3].reduce(max)) // 3

sum

Calculates sum of numbers in array

import { Reducers } from 'declarative-js'
import sum = Reducers.sum

[1, 2, 3].reduce(sum)) // 6

Reducer.Map

Returns map that is used Reducer.groupBy, Reducer.toMap, as a second parameter after callback. As this map has methods entries, keys, values (docs) it is simple to chain functions without calling Object.keys instead, if object is returned.

import { Reducers } from 'declarative-js'
import toMap = Reducers.toMap
import Map = Reducers.Map

[{name: 'john'}, {name: 'mike'}]
    .reduce(toMap(va => va.name), Map()) 
    //returns instance of {@link MethodMap}
    .entries()
    ...

Reducer.ImmutableMap

Returns immutable map that is used Reducer.groupBy, Reducer.toMap, as a second parameter after callback. As this map has methods entries, keys, values (docs) it is simple to chain functions without calling Object.keys instead, if object is returned.

import { Reducers } from 'declarative-js'
import toMap = Reducers.toMap
import ImmutableMap = Reducers.ImmutableMap

[{name: 'john'}, {name: 'mike'}]
    .reduce(toMap(va => va.name), ImmutableMap()) 
    //returns instance of {@link MethodMap}
    .entries()
    ...

Reducer.ImmutableObject

Returns immutable object that is used Reducer.toObject as a second parameter after callback.

import { Reducers } from 'declarative-js'
import toObject = Reducers.toObject
import ImmutableObject = Reducers.ImmutableObject

[{name: 'john'}, {name: 'mike'}]
    .reduce(toObject(va => va.name), ImmutableObject()) 
    ...

Filters

API documentation link

toBe.present

import { toBe } from 'declarative-js'

[undefined, 'a', 'b', null].filter(toBe.present) // ['a', 'b']

toBe.notEmpty

import { toBe } from 'declarative-js'

['', 'a', 'b'].filter(toBe.notEmpty) // ['a', 'b']
[[], ['a'], ['b']].filter(toBe.notEmpty) // [['a'], ['b']]

toBe.equal

import { toBe } from 'declarative-js'

['', 'a', 'b', 'a', 'c'].filter(toBe.equal('a')) // ['a', 'a']

toBe.notEqual

import { toBe } from 'declarative-js'

['a', 'b', 'a', 'c'].filter(toBe.notEqual('a')) // ['b', 'c']

toBe.unique

it works on primitives and objects as well. This function comparing references and content. So if some heavy objects must be compared, this function can be expensive.

performance benchmark: link

import { toBe } from 'declarative-js'

['a', 'b', 'a', 'a', 'c'].filter(toBe.unique()) // ['a', 'b', 'c']

performance benchmark for uniqueness by object content: link

import { toBe } from 'declarative-js'

[{a: 1}, {a: 1}, {a: 2}].filter(toBe.unique()) // [{a: 1}, {a: 2}]

toBe.uniqueBy

Less expensive function toBe.uniqueBy, when some unique identifier is set by user.

performance benchmark: link

import { toBe } from 'declarative-js'

const data = [
 { title: 'Predator', genre: 'sci-fi' },
 { title: 'Predator 2', genre: 'sci-fi'},
 { title: 'Alien vs Predator', genre: 'sci-fi' }, 
 { title: 'Tom & Jerry', genre: 'cartoon' } 
]
data.filter(toBe.uniqueBy(movie => movie.genre))
data.filter(toBe.uniqueBy('genre'))
// [
//  { title: 'Predator', genre: 'sci-fi' },
//  { title: 'Tom & Jerry', genre: 'cartoon' }
// ]

takeWhile

Function to be used in Array#filter function as a callback. It will pass items from array, while predicate matches. When predicate returns false none of the items will pass.

import {toBe} from 'declarative-js'
import takeWhile = Filter.takeWhile

function isScienceFiction(film) {
    return film.genre === 'sci-fi'
}

const films = [
 { title: 'Predator', genre: 'sci-fi' },
 { title: 'Predator 2', genre: 'sci-fi'},
 { title: 'Tom & Jerry', genre: 'cartoon' }, 
 { title: 'Alien vs Predator', genre: 'sci-fi' }
]

films.filter(takeWhile(isScienceFiction))
// =>
// [
//  { title: 'Predator', genre: 'sci-fi' },
//  { title: 'Predator 2', genre: 'sci-fi' }
// ]

skipWhile

Function to be used in Array#filter function as a callback. It will skip items from array, while predicate matches. When predicate returns {@code false}, other items will be returned form that point.

import {toBe} from 'declarative-js'
import skipWhile = Filter.skipWhile

function isScienceFiction(film) {
    return film.genre === 'sci-fi'
}

const films = [
 { title: 'Predator', genre: 'sci-fi' },
 { title: 'Predator 2', genre: 'sci-fi'},
 { title: 'Tom & Jerry', genre: 'cartoon' }, 
 { title: 'Alien vs Predator', genre: 'sci-fi' }
]

films.filter(skipWhile(isScienceFiction))
// =>
// [
//  { title: 'Tom & Jerry', genre: 'cartoon' }, 
//  { title: 'Alien vs Predator', genre: 'sci-fi' }
// ]

skipOnError

Skips an element, if predicate is resolving to false or an error occurred predicate will also resolve to false

Ignoring error

import {toBe} from 'declarative-js'
import skipOnError = Filter.skipOnError

const array = [1, 2, 3, 4, 5]
const result = array
    .filter(skipOnError(x => {
        if (x === 3) {
            throw new Error()
        }
        return x % 2 != 0
    }))
// [1, 5]

Consuming error

import {toBe} from 'declarative-js'
import skipOnError = toBe.skipOnError

function filterNone(x) {
    if (x === 3) {
        throw new Error('Invalid number')
    }
    return true
}

const array = [1, 2, 3, 4, 5]
const result = array
    .filter(skipOnError(
        filterNone,
        (error, element, index) => console.warn({error, element, index})
    ))
// console.warn: { error, [Error], element: 3, index: 2}
// [1, 2, 4, 5]

Mappers

API documentation link

toObjValues

As javascript Object class has static method keys, there is similar method to get object values

import { Mapper } from 'declarative-js'
import toObjValues = Reducers.toObjValues

[{a: 1, b: 2}, {a: 3, b: 4}].map(toObjValues) // [[1, 2], [3, 4]]

Sorters

API documentation link

performance benchmark: link

ascendingBy

Sorts array in ascending order by values provided from callbacks. First callback has highest priority in sorting and so on.

import { Sort } from 'declarative-js'
import ascendingBy = Sort.ascendingBy

names.sort(ascendingBy(
    x => x.name, 
    x => x.lastName, 
    x => x.age
));
names.sort(ascendingBy('name', 'lastName', 'age'));
// sorted by name, lastName and age 
// [
//    { name: 'andrew', lastName: 'Aa', age: 1 },
//    { name: 'andrew', lastName: 'Bb', age: 1 },
//    { name: 'andrew', lastName: 'Bb', age: 2 },
//    { name: 'billy', lastName: 'Cc', age: 1 },
//    { name: 'billy', lastName: 'Cc', age: 5 },
// ]                

descendingBy

Sorts array in descending order by values provided from callbacks. First callback has highest priority in sorting and so on.

import { Sort } from 'declarative-js'
import descendingBy = Sort.descendingBy

names.sort(descendingBy(
    x => x.name, 
    x => x.lastName, 
    x => x.age
));
names.sort(descendingBy('name', 'lastName', 'age'));

// sorted by name, lastName and age
// [
//    { name: 'billy', lastName: 'Cc', age: 5 },
//    { name: 'billy', lastName: 'Cc', age: 1 },
//    { name: 'andrew', lastName: 'Bb', age: 2 },
//    { name: 'andrew', lastName: 'Bb', age: 1 },
//    { name: 'andrew', lastName: 'Aa', age: 1 }
// ]                

by

Function that will sort items in array with custom values, by provided order. It accepts as a parameter object with valueToOrderElement mapper and array of custom order rule

import { Sort } from 'declarative-js'
import by = Sort.by

const result = testTodoData.sort(by(
    { toValue: x => x.severity, order: ['low', 'medium', 'high'] },
    { toValue: x => x.task, order: ['Sleep', 'Drink'] }
  ))
// { task: 'Sleep', severity: 'low' },
// { task: 'Drink', severity: 'low' },
// { task: 'Eat', severity: 'medium' },
// { task: 'Code', severity: 'high' },
import { Sort } from 'declarative-js'
import by = Sort.by

const result = testTodoData.sort(by('severity', ['low', 'medium', 'high']))
// { task: 'Sleep', severity: 'low' },
// { task: 'Drink', severity: 'low' },
// { task: 'Eat', severity: 'medium' },
// { task: 'Code', severity: 'high' },

orderedBy

Function that will sort items in array, by provided order. It accepts as a parameter array of custom order rule. Element, that are not present in order array will be at he the end of the sorted list.

import { Sort } from 'declarative-js'
import orderedBy = Sort.orderedBy

const testData = 
    ['bar', 'medium', 'foo', 'low']
const result = 
    testData.sort(orderedBy(['low', 'medium', 'high']))
// result => ['low', 'medium', 'bar', 'foo', ]

Optional

API documentation link

Idea of this function is from Java Optional This function checks value to be non null or undefined. It has two branches of functions, .map(x) when value is present and second when value is absent .or.x

toArray

Converts value to array. If value is not present returns empty array. If value is single object returns array of one object . If value is array returns array.

    import { optional } from 'declarative-js'

    optional('hi').toArray() // ['hi']
    optional(['hi', 'Mr.']).toArray() // ['hi', 'Mr.']
    optional(undefined).toArray() // []

isAbsent

    isAbsent() //true or false

ifAbsent

    optional(myVar).ifAbsent(() => console.warn('I am not here'))

isPresent

    optional(myVar).isPresent() //true or false

ifPresent

    optional(myVar).ifPresent(() => console.warn('I am here'))

or

import { optional } from 'declarative-js'

// instant  
optional(myVar).orElse('Alternative')
// lazy
optional(myVar).orElseGet(() => 'Alternative')
// error
optional(myVar).orElseThrow('This is bad')

map

Every map call is checking is mapped value defined. If mapped value is undefined, other map calls will not be executed.

    import { optional } from 'declarative-js'

    const toGreeting = name => `Hi, ${name}!`
    optional(myVar)
        .map(x => x.event)
        .map(event => event.name)
        .map(toGreeting)
        .get() // if some map evaluated to undefined an error will be thrown

filter

Method predicate (value: T) => boolean. If filters predicate returns false, other piped filter or map calls will no be executed.

    import { optional } from 'declarative-js'

    const toGreeting = name => `Hi, ${name}!`
    optional(myVar)
        .map(x => x.event)
        .map(event => event.name)
        .filter(name => name === 'John')
        .map(toGreeting)
        .get() // if filter returned false an error will be thrown

MethodMap

API documentation link

Interface for DTO to that is used in reducers. Provided two implementations: JMap ImmutableMap

interface MethodMap<T> {
    put(key: string, value: T): void
    get(key: string): T | undefined
    keys(): string[]
    values(): T[]
    containsKey(key: string): boolean
    containsValue(value: T): boolean
    entries(): Entry<T>[]
    size(): number
    toObject(): {[keyof: string]: T}
}

JMap

API documentation link

Map that has all required functions to comfortably work with it. Implements typescript interface MethodMap

const jmap = new JMap()
jmap.put('mike', 1)
jmap.put('john', 2)

sample.keys() // ['mike', 'john']
sample.values() // [1, 2]
sample.size() // 2
sample.get('mike') // 1
sample.containsValue(1) // true
sample.containsKey('mike') //false
sample.entries() // [ {key: 'mike', value: 1}, {key: 'john', value: 2} ]

This map can be created from object as well.

const map = new JMap({a: 1, b: 2})