This week I released new versions of my RxJS libraries in rxjs-primitives. Since it’s released I’ve added a few new utility operators. Some of these have been out for a while since I originally wrote about the release, so I’ve highlighted them here as they may be useful to some developers.

You can check out the full docs here.

Typescript 4

Upgrading to Typescript 4 has allowed the removal of polymorphic functions in place of Vardic Tuple Types and is why there is a major bump on all packages.

This can be seen in the old and new concat operator in the rxjs-string package.

Most of the tests have also been converted to rxjs-marbles allowing for more robust Observable testing (if you are working with RxJS I highly recommend checking it out, it integrates well with runners like Jest).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
it(
    'should return string value of string ending with passed character',
    marbles((m) => {
      const input = m.hot('-a-b-c-|', { a: 'test', b: 'testing', c: 'gone' });
      const subs = '^------!';
      const expected = m.cold('---y---|', { y: 'testing' });
      m.expect(input.pipe(filterEndsWith('g'))).toBeObservable(expected);
      m.expect(input).toHaveSubscriptions(subs);
    }),
  );

rxjs-array

npm install @tinynodes/rxjs-array

In the array module there are some operators to use with finding the difference or intersection between a source and a passed array, for example:

1
2
3
4
5
6
7
of(['a', 'b', 'd'])
 .pipe(difference(['a', 'c']))
 .subscribe(console.log) // ['b', 'd']

of(['a', 'b', 'd'])
 .pipe(intersects(['a', 'c']))
 .subscribe(console.log) // ['a']

These methods accept an array, or an Observable<Array> of items to compare against.

The module also included a binarySearch operator which returns a custom BinarySearchResult tuple.

rxjs-boolean

npm install @tinynodes/rxjs-boolean

A new Luhn algorithm operator luhnCheck is provided that does validation on numbers such as credit cards, ID cards and other value schemes that use the check.

1
2
3
fromString('4485275742308327')
    .pipe(luhnCheck())
    .subscribe(console.log) // true, this is a valid credit card

rxjs-number

npm install @tinynodes/rxjs-number

inRange / outOfRange and filterInRange / filterOutOfRange both all two numbers, the filter methods return the value from the source observable within the range of those values, while the other methods return a boolean value if in range. An optional third value will include/exclude the range value based on the method

1
2
3
4
5
6
7
8
fromNumber([-1, 0, 1, 2, 10, 11])
 .pipe(filterInRange(0, 10))
 .subscribe(console.log) // [0, 1, 2, 10]

// Passing true as the third parameter, the range numbers will also be excluded
fromNumber([-1, 0, 1, 2, 10, 11])
 .pipe(filterInRange(0, 10, true))
 .subscribe(console.log) // [1, 2]

rxjs-string

npm install @tinynodes/rxjs-string

New operators such as titleize, repeat and match add new utility features for strings. Where they can they also support localisation:

1
2
3
4
5
6
7
fromString('Mary had a little lamb')
 .pipe(titleize())
 .subscribe(console.log) // 'Mary Had A Little Lamb'

fromString('Mary had ä little lamb')
 .pipe(titleize('de-DE'))
 .subscribe(console.log) // 'Mary Had Ä Little Lamb'

rxjs-utility

npm install @tinynodes/rxjs-utility

The utility module contains some specialised tap operators such as tapIf, startWithTap and tapOnSubscribe. These provide a way to do side effects. With startWithTap it can be used with Angular to do a form touch, also tapOnSubscribe will fire when there is a subscription to the Observable:

1
2
3
4
5
6
7
8
9
// Only touch on first value change
form.valueChange.pipe(
 startWithTap(() => this.onTouch())
).subscribe()

// Fire when a component subscribes to the service bus
this.serviceBus.pipe(
  tapOnSubscribe((name: string) => console.log(`New Subscription to ${name}`))
).subscribe()

The tapIf will only fire if a passed method result is truthy:

1
2
3
fromNumber([1, 2, 3, 4, 5, 6]).pipe(
  tapIf((val) => val % 2 === 0), (val) => console.log(val)
).subscribe() // 2, 4, 6

The last operator is mapIfSource which might be a bit of a weird one but I hope might become useful.

The operator takes the value from the source and passes to a predicate method, and depending on the result will map the result of a passed method. A simple example would be:

1
2
3
4
5
6
7
fromNumber([1, 2, 3, 4, 5, 6]).pipe(
  mapIfSource(
    (value) => val % 2 === 0,
    (value) => val * 10,
    (value) => val * 20
  )
).subscribe() // 20, 20, 60 40, 100, 60

Here, if the result of the predicate is true multiply by 10, otherwise by 20. The method is typed to allow different return values based on the result (so you will have to handle the type later). For example we could even turn it into a FizzBuzz operator:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
export function fizzbuzz(): OperatorFunction<number, string | number> {
  return (source: Observable<number>) =>
    source.pipe(
      mapIfSource<number, string, number>(
        (value) => value % 15 == 0 || value % 3 == 0 || value % 5 == 0,
        (value) => (value % 15 == 0 ? `FizzBuzz` : value % 3 === 0 ? 'Fizz' : 'Buzz'),
        (value) => value
     )
  );
}

// And now we use it in our code
fromNumber([1, 3, 5, 15, 16]).pipe(
  fizzbuzz(),
).subscribe() // 1, 'Fizz', 'Buzz', 'FizzBuzz', 16

Hopefully you’ll find these operators useful and feel free to leave feedback and suggestions.