Simple monad like wrapper.
See the ChainContainer for more.
Very helpful for taking values and then progressively working on them, instead of continually wrapping deeper in method calls.
Calling chain(3)
- gives an object you can then chain calls against:
- .close() - gets the value of the current chain
- .chain(function) - where it is passed the value, and returns a new Chain with that value.
- .errorHandler(fn) - custom function called if an error is ever thrown
- .debug() - console.logs the current value, and continues the chain with that value
Along with methods that can iterate on each element, assuming the value in the chain is an Array.
- .chainMap(function) - where it calls the
.map
on value, and applies the function on every item in the array, storing the result from the function.
(Useful for changing values without changing the original object)) - .chainForEach(function) - where calls
.forEach
on value, and applies the function on every item, without storing the result from the function.
(Useful for changing objects in-place) - .chainFlatMap(function) - where it calls
.flatMap
on value, and applies the function on every item, flattening the results.
(Useful for expanding an array based on values in the array) - .chainFilter(function) - where it calls
.filter
on value, using the function on every item, keeping the item in the list if the function returns true.
(Useful for removing items from an array) - .chainReduce(function, initialValue) - where it calls
.reduce
on value, and reduces the value to a single result.
(Useful for reducing the array to a single value
- like a concatenated string or sum total)
There may be times you want to run side effects, or replace the value entirely. (This isn't common, but may be useful on occasion)
- .execute(function) - where it calls a function, but doesn't pass on the result.
(This is useful for side-effects, like writing to files) - .replace(value) - replaces the value in the chain with a literal value, regardless of the previous value.
For example:
addTwo = (value) => value + 2;
//-- we can always get the value
utils.chain(3).close(); // 3
but this is much easier if we continue to chain it
addTwo = (value) => value + 2;
addTwo(3); // 5
utils.chain(3)
.chain(addTwo) // (3 + 2)
.chain(addTwo) // (5 + 2)
.debug() // consoles 7 and passes the value along
// define a function inline
.chain((value) => value + 3) // (7 + 3)
.close()
// consoles out value `7`
// returns value 10
Note that we can also map against values in the array
initializeArray = (size) => Array.from(Array(size)).map((val, index) => index);
initializeArray(3); // [0, 1, 2]
addTwo = (value) => value + 2;
addTwo(3); // 5
utils.chain(3)
.chain(initializeArray) // [0, 1, 2]
.chainMap(addTwo) // [2, 3, 4] or [0 + 2, 1 + 2, 2 + 2]
.chainMap(addTwo)
.close();
// [4, 5, 6]
Chain to log results while transforming values
results = [{ userId: 'abc123' }, { userId: 'xyz987' }];
activeUsers = chain(results)
.chainMap((record) => users.get(record.userId))
.chainForEach(record => record.status = 'active')
.chain(records => d3.csv.format(records))
.execute(records => utils.file.writeFile('./log', d3.csv.format(records)))
.close()
Or even combine with other utility methods
badStr = 'I%20am%20the%20very%20model%20of%20a%20modern%20Major'
+ '-General%0AI\'ve%20information%20vegetable%2C%20animal%2C%20'
+ 'and%20mineral%0AI%20know%20the%20kings%20of%20England%2C%20'
+ 'and%20I%20quote%20the%20fights%0AHistorical%0AFrom%20Marath'
+ 'on%20to%20Waterloo%2C%20in%20order%20categorical';
chain(badStr)
.chain(decodeURIComponent)
.chain(v => v.split('\n'))
// .debug() // check the values along the way
.chainMap(line => ({ line, length: line.length }))
.chain(values => utils.table(values).render());
this can be more legible than the normal way to write this,
especially if you need to troubleshoot the value halfway through.
utils.table(
decodeURIComponent(badStr)
.split('\n')
.map(line => ({ line, length: line.length }))
).render()
and it renders out a lovely table like this:
line | length |
---|---|
I am the very model of a modern Major-General | 45 |
I've information vegetable, animal, and mineral | 47 |
I know the kings of England, and I quote the fights | 51 |
Historical | 10 |
From Marathon to Waterloo, in order categorical | 47 |