stateful-predicates
Version:
Bunch of stateful predicate wrappers. RxJS & Array.filter compliant.
224 lines (165 loc) • 6.34 kB
Markdown

[](https://codecov.io/gh/tomaskraus/stateful-predicates)
[](https://github.com/google/gts)
# stateful-predicates
Carefully selected, minimalistic collection of predicate wrappers.
`stateful-predicates` bring new power to standard _predicates_, required by [Array.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), [RxJS](https://rxjs.dev/api/operators/takeWhile) or other methods/libraries that use predicates.
Predicate list:
- [switchTrueFalse](#switchtruefalse)
- [oneAfter](#oneafter)
- [nthMatch](#nthmatch)
- [onChange](#onchange)
**Example**: Get an inside of a block documetation comment:
```ts
import {switchTrueFalse, oneAfter} from 'stateful-predicates';
// prettier-ignore
const linesInsideADocBlockComment = lines.filter(
switchTrueFalse(
oneAfter(s => /\/\*\*/i.test(s)), // start to "return true" one line after a `/**`
s => /\*\//i.test(s) // start to "return false" on a line with `*/`
)
);
```
see a [complete example](#complete-example)
`stateful-predicates` library is all about [predicates](#tpredicate) applied to **elements** of some **list**. That list is typically an [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators#iterables).
When a _predicate_ is applied to an _element_, that _predicate_ can either **succeed** on that _element_ (i.e. **match** that _element_), or not.
## Why to use
- No state variable mess as it uses _closures_ to preserve the state.
- Functional programming friendly.
- Typed. With `d.ts` for Javascript.
- Well tested. 100% code coverage.
## Installation
```bash
$ npm install stateful-predicates
```
## Usage
Typescript / ES module:
```ts
import * as SP from 'stateful-predicates';
```
Javascript / CommonJS:
```js
const SP = require('stateful-predicates');
```
## API
### TPredicate
```ts
type TPredicate<T> = (value: T, index: number) => boolean;
```
Predicate is a function, that accepts some value and returns a boolean value, based on its condition.
It's something you can pass as callback to [Array.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) or [RxJS](https://rxjs.dev/api/operators/takeWhile) operators.
Almost every function of `stateful-predicates` library accepts at least one `TPredicate` argument and returns another `TPredicate` value.
### switchTrueFalse
```ts
function switchTrueFalse<T>(
predicateForTrue: TPredicate<T>,
predicateForFalse: TPredicate<T>
): TPredicate<T>;
```
Returns a predicate(value, index) `P` that fulfills the following:
1. `P` stays _true_ "on and after" `predicateForTrue` has succeeded on some element
2. `P` becomes _false_ again "on and after" `predicateForFalse` has succeeded on some element that follows.
At the beginning, `P` is _false_.
3. `P` is **reusable**: able to switch _true_/_false_ multiple times.
4. `P` is **greedy**:
- switches to _true_ on the first of consecutive elements `predicateForTrue` can succeed
- switches to _false_ on the first of consecutive elements `predicateForFalse` can succeed
**Example**:
```ts
const elementsEnclosedByZeroAndMinusOne = [2, 1, 0, 0, 5, 9, -1, -1, 7].filter(
switchTrueFalse(
x => x === 0,
x => x === -1
)
);
console.log(elementsEnclosedByZeroAndMinusOne);
//=> [ 0, 0, 5, 9 ]
```
### oneAfter
```ts
function oneAfter<T>(innerPredicate: TPredicate<T>): TPredicate<T>;
```
Returns _predicate(value, index)_ `P`, that:
- returns _true_ if its `innerPredicate` has succeeded at previous element, i.e element with index-1.
**Example**:
```ts
const isThree = (x: number) => x === 3;
const result = [2, 3, 3, 0].map(oneAfter(isThree));
console.log(result);
//=> [ false, false, true, true ]
```
It kind of shifts (or delays) the succesful element evaluation:
```ts
const resultsWithoutOneAfter = [2, 3, 3, 0].map(isThree);
console.log(resultsWithoutOneAfter);
//=> [ false, true, true, false ]
```
### nthMatch
```ts
function nthMatch<T>(n: number, innerPredicate: TPredicate<T>): TPredicate<T>;
```
Returns predicate(value, index) `P`, that:
- returns _true_ if its `innerPredicate` has succeeded `n` times
**Example**:
```ts
const isEven = x => x % 2 === 0;
const secondMatchingElem = [2, 3, 5, 4, 8, 5, -8].filter(nthMatch(2, isEven));
console.log(secondMatchingElem);
//=> [ 4 ]`
```
### onChange
```ts
function onChange<T>(innerPredicate: TPredicate<T>): TPredicate<T>;
```
Returns predicate(value, index) `P`, that:
- returns _true_ whenever its `innerPredicate` changes value - i.e. result of `innerPredicate` differs from `P`'s internal state.
At the begin, the internal state of `P` is _false_.
**Example**:
```ts
const isThree = (x: number) => x === 3;
const changes = [2, 3, 3, 3, 4, 3, 5, -8].map(onChange(isThree));
console.log(changes);
//=> [ false, true, false, false, true, true, true, false ]
```
## Complete Example
Show only documentation comments from a source code input text:
```ts
import {switchTrueFalse, oneAfter} from 'stateful-predicates';
const input = `
/**
* greaterThanOne
* @param x number value
* @returns true if x is greater than one, false otherwise
*/
*/
function greaterThanOne(x: number): boolean {
return x > 1;
}
/**
* An increment function
* @param x number value
* @returns that value incremented by one
*/
const inc = (x: number) => ++x;`;
const docCommentPredicate = () =>
switchTrueFalse<string>(
s => /\/\*\*/.test(s), // true at '/**' (begin-mark)
oneAfter(s => /\*\//.test(s)) // false after '*/' (end-mark)
);
// prettier-ignore
const onlyDocComments = input
.split('\n')
.filter(docCommentPredicate())
.join('\n');
console.log(onlyDocComments);
//=> /**
// * greaterThanOne
// * @param x number value
// * @returns true if x is greater than one, false otherwise
// */
// /**
// * An increment function
// * @param x number value
// * @returns that value incremented by one
// */
```