| 77 | * @docsCategory operators |
| 78 | */ |
| 79 | export function selectSlice<T extends object, K extends keyof T>( |
| 80 | keys: K[], |
| 81 | keyCompareMap?: KeyCompareMap<{ [P in K]: T[P] }> |
| 82 | ): OperatorFunction<T, PickSlice<T, K>> { |
| 83 | return (o$: Observable<T>): Observable<PickSlice<T, K>> => |
| 84 | o$.pipe( |
| 85 | filter((state) => state !== undefined), |
| 86 | map((state) => { |
| 87 | // forward null |
| 88 | if (state === null) { |
| 89 | return null; |
| 90 | } |
| 91 | // an array of all keys which exist and are _defined_ in the state object |
| 92 | const definedKeys = keys |
| 93 | // filter out undefined properties e. g. {}, { str: undefined } |
| 94 | .filter((k) => state.hasOwnProperty(k) && state[k] !== undefined); |
| 95 | |
| 96 | // we want to ensure to only emit _valid_ selections |
| 97 | // a selection is _valid_ if every selected key exists and has a value: |
| 98 | |
| 99 | // {} => selectSlice(['foo']) => no emission |
| 100 | // {str: 'test'} => selectSlice([]) => no emission |
| 101 | // {str: 'test'} => selectSlice(['notPresent']) => no emission |
| 102 | // {str: 'test'} => state.select(selectSlice([])) => no emission |
| 103 | // {str: 'test'} => state.select(selectSlice(['notPresent'])) => no emission |
| 104 | // {str: undefined} => state.select(selectSlice(['str'])) => no emission |
| 105 | // {str: 'test', foo: undefined } => state.select(selectSlice(['foo'])) => no emission |
| 106 | if (definedKeys.length < keys.length) { |
| 107 | return undefined; |
| 108 | } |
| 109 | |
| 110 | // create the selected slice |
| 111 | return definedKeys.reduce((vm, key) => { |
| 112 | vm[key] = state[key]; |
| 113 | return vm; |
| 114 | }, {} as PickSlice<T, K>); |
| 115 | }), |
| 116 | filter((v) => v !== undefined), |
| 117 | distinctUntilSomeChanged(keys, keyCompareMap) |
| 118 | ); |
| 119 | } |