Skip to content

Commit

Permalink
refactor selectSlice, distinctUntilSomeChanged, keyCompareMap (#159)
Browse files Browse the repository at this point in the history
* refactor(state): refactored selectSlice, distinctUntilSomeChanged and keyCompareMap signatures

* fix(state): KeyCompareMap adoption

* docs(state): fixed jsDocs after refactor

Co-authored-by: Michael Hladky <michael@hladky.at>
  • Loading branch information
hoebbelsB and BioPhoton committed Jun 30, 2020
1 parent 4da21ef commit 0e6e2f3
Show file tree
Hide file tree
Showing 9 changed files with 478 additions and 515 deletions.
108 changes: 25 additions & 83 deletions libs/state/docs/api/operators/distinct-until-some-changed.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,10 @@
# distinctUntilSomeChanged

Returns an Observable that emits all items emitted by the source Observable that are distinct by comparison from
the previous item. You can provide a custom comparison for each key individually by setting a `KeyCompareMap<T>`.
If no comparison is provided for a specified key, an equality check is used by default.

If properties of the source change, which are not specified for comparison, no change will be emitted.

The name `distinctUntilSomeChanged` was picked since it internally iterates over the `keys` and utilizes the
[some](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/some) method in order to
compute if values are distinct or not.

_Example_

```TypeScript
// An example comparing the first letters of just the name property.

import { of } from 'rxjs';
import { distinctUntilSomeChanged } from 'rx-angular/state';

interface Person {
age: number;
name: string;
}
// compare the first letters of the name property
const customComparison: KeyCompareMap<Person> = {
name: (oldName, newName) => oldName.substring(0, 3) === newName.substring(0, 3)
};

of<Person>(
{ age: 4, name: 'Foo1'},
{ age: 7, name: 'Bar'},
{ age: 5, name: 'Foo2'},
{ age: 6, name: 'Foo3'},
).pipe(
distinctUntilSomeChanged(customComparison),
)
.subscribe(x => console.log(x));

// displays:
// { age: 4, name: 'Foo1' }
// { age: 7, name: 'Bar' }
// { age: 5, name: 'Foo2' }
```

## Signature

```TypeScript
function distinctUntilSomeChanged<T extends object, K extends keyof T>(keyCompareMap: KeyCompareMap<T>): MonoTypeOperatorFunction<T>
```

## Parameters

### keyCompareMap

##### typeof: KeyCompareMap&#60;T&#62;

# distinctUntilSomeChanged

Returns an Observable that emits all items emitted by the source Observable that are distinct by comparison from
the previous item. Comparison will be done for each set key in the `keys` array.

If a comparator function is provided, then it will be called for each item to test for whether or not that value should be emitted.
If properties of the source change which are not specified for comparison, no change will be emitted.
You can fine grain your distinct checks by providing a `KeyCompareMap` with those keys you want to compute
explicitly different

The name `distinctUntilSomeChanged` was picked since it internally iterates over the `keys` and utilizes the
[some](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/some) method in order to
Expand All @@ -79,58 +22,57 @@ interface Person {
}

of(
{ age: 4, name: 'Foo'},
{ age: 7, name: 'Bar'},
{ age: 5, name: 'Foo'},
{ age: 6, name: 'Foo'},
{ age: 4, name: 'Hans'},
{ age: 7, name: 'Sophie'},
{ age: 5, name: 'Han Solo'},
{ age: 5, name: 'HanSophie'},
).pipe(
distinctUntilSomeChanged(['age', 'name']),
)
.subscribe(x => console.log(x));

// displays:
// { age: 4, name: 'Foo' }
// { age: 7, name: 'Bar' }
// { age: 5, name: 'Foo' }
// { age: 6, name: 'Foo' }
// { age: 4, name: 'Hans'}
// { age: 7, name: 'Sophie'}
// { age: 5, name: 'Han Solo'}
// { age: 5, name: 'HanSophie'}
```

_Example_

```TypeScript
// An example with a custom comparison applied to each key
// An example with `KeyCompareMap`
import { of } from 'rxjs';
import { distinctUntilSomeChanged } from 'rxjs/operators';
import { isDeepEqual } from 'custom/is-equal';

interface Person {
age: number;
name: string;
}
const customCompare = (oldVal, newVal) => isDeepEqual(oldVal, newVal);
const customComparison: KeyCompareMap<Person> = {
name: (oldName, newName) => oldName.substring(0, 2) === newName.substring(0, 2)
};

of(
{ age: 4, name: 'Foo1'},
{ age: 7, name: 'Bar'},
{ age: 5, name: 'Foo2'},
{ age: 6, name: 'Foo3'},
{ age: 4, name: 'Hans'},
{ age: 7, name: 'Sophie'},
{ age: 5, name: 'Han Solo'},
{ age: 5, name: 'HanSophie'},
).pipe(
distinctUntilSomeChanged(['age', 'name'], customCompare),
distinctUntilSomeChanged(['age', 'name'], customComparison),
)
.subscribe(x => console.log(x));

// displays:
// { age: 4, name: 'Foo1' }
// { age: 7, name: 'Bar' }
// { age: 5, name: 'Foo2' }
// { age: 6, name: 'Foo3' }
// { age: 4, name: 'Hans' }
// { age: 7, name: 'Sophie' }
// { age: 5, name: 'Han Solo' }
```

## Signature

```TypeScript
function distinctUntilSomeChanged<T extends object, K extends keyof T>(keys: K[], compare?: CompareFn<T[K]>): MonoTypeOperatorFunction<T>
function distinctUntilSomeChanged<T extends object, K extends keyof T>(keys: K[], keyCompareMap?: KeyCompareMap<T>): MonoTypeOperatorFunction<T>
```

## Parameters
Expand All @@ -139,6 +81,6 @@ function distinctUntilSomeChanged<T extends object, K extends keyof T>(keys: K[]

##### typeof: K[]

### compare
### keyCompareMap

##### typeof: CompareFn&#60;T[K]&#62;
##### typeof: KeyCompareMap&#60;T&#62;
20 changes: 10 additions & 10 deletions libs/state/docs/api/operators/interfaces.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
# CompareFn

The function which is used by `distinctUntilSomeChanged` and `selectSlice` to determine if changes are distinct or
not.
The function which is used by `KeyCompareMap` to determine if changes are distinct or not.
Should return true if values are equal.

## Signature

```TypeScript
type CompareFn<T> = (oldVal: T, newVal: T) => boolean
type CompareFn<T> = (
oldVal: T | null | undefined,
newVal: T | null | undefined
) => boolean
```
# KeyCompareMap
The `KeyCompareMap` is used to configure custom comparison for defined keys. You can set the `CompareFn` to
`undefined` in order to utilize the default equality check.
The `KeyCompareMap` is used to configure custom comparison for defined keys.
_Example_
```TypeScript
const keyCompareMap = {
myKey: (o, n) => customCompare(o, n),
myOtherKey: undefined
myKey: (o, n) => customCompare(o, n)
};
const o$ = of({
myKey: 5,
myOtherKey: 'bar'
}).pipe(distinctUntilSomeChanged(keyCompareMap));
}).pipe(distinctUntilSomeChanged(['myKey', 'myOtherKey'], keyCompareMap));

//or

const o$ = of({
myKey: 5,
myOtherKey: 'bar'
}).pipe(selectSlice(keyCompareMap));
}).pipe(selectSlice(['myKey', 'myOtherKey'], keyCompareMap));
```

## Signature

```TypeScript
type KeyCompareMap<T extends object> = {
[K in keyof T]?: CompareFn<T[K]>;
[K in keyof Partial<T>]: CompareFn<T[K]>;
}
```
127 changes: 38 additions & 89 deletions libs/state/docs/api/operators/select-slice.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,38 @@
# selectSlice

Returns an Observable that emits a distinct subset of the received object.
You can provide a custom comparison for each key individually by setting a `KeyCompareMap<T>`.
If no comparison is provided for a specified key, an equality check is used by default.
Returns an Observable that emits only the provided `keys` emitted by the source Observable. Each key will get
filtered to only emit _defined_ values as well as checked for distinct emissions.
Comparison will be done for each set key in the `keys` array.

You can fine grain your distinct checks by providing a `KeyCompareMap` with those keys you want to compute
explicitly different

_Example_

```TypeScript
import { of, Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
// An example with a custom comparison applied to each key
import { of } from 'rxjs';
import { selectSlice } from 'rx-angular/state';

interface MyState {
title: string;
items: string[];
panelOpen: boolean;
}

// Select title and panelOpen.
// compare the first letters of the `title` property and use the default comparison for `panelOpen`
const customComparison: KeyCompareMap<MyState> = {
title: (oldTitle, newTitle) => oldTitle.substring(0, 3) === newTitle.substring(0, 3),
panelOpen: undefined
};

const state$: Observable<MyState> = of(
{ title: 'myTitle', items: ['foo', 'bar'], panelOpen: true},
{ title: 'myTitle2', items: ['foo', 'bar'], panelOpen: true},
{ title: 'newTitle', items: ['foo', 'bar'], panelOpen: true},
{ title: 'newTitle', items: ['foo', 'bar'], panelOpen: false}
);
const viewModel$ = state$.pipe(
selectSlice(customComparison),
tap(console.log)
).subscribe();
{ title: 'myTitle', panelOpen: true},
{ title: 'myTitle2', panelOpen: true},
{ title: 'newTitle', panelOpen: true},
{ title: 'newTitle', panelOpen: false}
)
.pipe(
selectSlice(['title', 'panelOpen']),
)
.subscribe(x => console.log(x));

// displays:
// { panelOpen: true, title: 'myTitle' }
// { panelOpen: true, title: 'newTitle' }
// { panelOpen: false, title: 'newTitle' }
// { title: 'myTitle', panelOpen: true },
// { title: 'myTitle2', panelOpen: true },
// { title: 'newTitle', panelOpen: true },
// { title: 'newTitle', panelOpen: false }
```

## Signature

```TypeScript
function selectSlice<T extends object, K extends keyof T, R>(keyCompareMap: KeyCompareMap<T>): OperatorFunction<T, R>
```

## Parameters

### keyCompareMap

##### typeof: KeyCompareMap&#60;T&#62;

# selectSlice

Returns an Observable that emits only the provided `keys` emitted by the source Observable. Each key will get
filtered to only emit _defined_ values as well as checked for distinct emissions.
Comparison will be done for each set key in the `keys` array.

If a comparator function is provided, then it will be called for each item to test for whether or not that value
should be emitted.

_Example_

```TypeScript
Expand All @@ -74,51 +45,29 @@ interface MyState {
items: string[];
panelOpen: boolean;
}
const state$: Observable<MyState> = of({
title: 'myTitle',
items: ['foo', 'bar'],
panelOpen: true
});
const slice$ = state$.pipe(selectSlice(['items', 'panelOpen'])).pipe(tap(console.log)).subscribe();
// displays:
// { items: ['foo', 'bar'], panelOpen: true }
*
```

_Example_

```TypeScript
// An example with a custom comparison applied to each key
import { of } from 'rxjs';
import { selectSlices } from 'rxjs/operators';
import { isDeepEqual } from 'custom/is-equal';
const customCompare = (oldVal, newVal) => isDeepEqual(oldVal, newVal);
// Select items and title.
// apply custom compare logic for the items array
const customComparison: KeyCompareMap<MyState> = {
items: (oldItems, newItems) => compareItems(oldItems, newItems)
};
const state$: Observable<MyState> = of(
{ title: 'myTitle', items: ['foo'], panelOpen: true},
{ title: 'myTitle2', items: ['foo', 'bar'], panelOpen: true},
{ title: 'newTitle', items: ['foo', 'baz'], panelOpen: true},
{ title: 'newTitle', items: ['foo', 'baz'], panelOpen: true}
)
.pipe(
selectSlices(['title', 'items'], customCompare),
)
.subscribe(x => console.log(x));
{ title: 'myTitle', items: ['foo', 'bar'], panelOpen: true },
{ title: 'myTitle', items: ['foo', 'bar'], panelOpen: false },
{ title: 'nextTitle', items: ['foo', 'baR'], panelOpen: true },
{ title: 'nextTitle', items: ['fooRz', 'boo'], panelOpen: false },
);
const slice$ = state$.pipe(selectSlice(['title', 'items']), tap(console.log)).subscribe();

// displays:
// { title: 'myTitle', items: ['foo'], panelOpen: true},
// { title: 'myTitle2', items: ['foo', 'bar'], panelOpen: true},
// { title: 'newTitle', items: ['foo', 'baz'], panelOpen: true},
// { title: 'myTitle', items: ['foo', 'bar'] }
// { title: 'nextTitle', items: ['foo', 'baR'] }
// { title: 'nextTitle', items: ['fooRz', 'boo'] }
```

## Signature

```TypeScript
function selectSlice<T extends object, K extends keyof T, R>(keys: K[], compare?: CompareFn<T[K]>): OperatorFunction<T, R>
function selectSlice<T extends object, K extends keyof T>(keys: K[], keyCompareMap?: KeyCompareMap<{ [P in K]: T[P] }>): OperatorFunction<T, PickStrict<T, K> | null>
```

## Parameters
Expand All @@ -127,6 +76,6 @@ function selectSlice<T extends object, K extends keyof T, R>(keys: K[], compare?

##### typeof: K[]

### compare
### keyCompareMap

##### typeof: CompareFn&#60;T[K]&#62;
##### typeof: KeyCompareMap&#60;{ [P in K]: T[P] }&#62;

0 comments on commit 0e6e2f3

Please sign in to comment.