How to call "defaultIfEmpty" when list is empty on RxJS?

Viewed 32

I've two lists with two distinct objects that need to be converted into the same type, the "second" list will be used only if the "first" list is empty, I tried to use the method defaultIfEmpty but it never return the second option.

const first = []; // could be [{code: 1}, {code: 2}]
const second = [{id: 1}, {id: 2}]

of(first).pipe(
    map((value) => {number: value.code})
).pipe(
    defaultIfEmpty(of(second).pipe(map((value) => {number: value.id})))
).subscribe(doSomething);

The desired output is:

[{number: 1}, {number: 2}]

On the example above, the map from defaultIfEmpty is never called;

  1. how can I "switch" to another method source if the given source is empty?
  2. will subscribe method be called after the map is complete, or it will be called for each item on map?
3 Answers

If that's an option just create the right observable at runtime:

const makeObservable =
  (arr1, arr2) =>
    from(arr1.length ? arr1 : arr2)
      .pipe(map(({code, id}) => ({number: code ?? id})));
  
const obs1$ = makeObservable([], [{id:1},{id:2}]);
const obs2$ = makeObservable([{code:2},{code:3}], []);

obs1$.subscribe(o => console.log(o));
obs2$.subscribe(o => console.log(o));
<script src="https://unpkg.com/rxjs@%5E7/dist/bundles/rxjs.umd.min.js"></script>
<script>
const {from} = rxjs;
const {map} = rxjs.operators;
</script>

const list1: { code: number }[] = [];
const list2 = [{ id: 1 }, { id: 2 }];

of(list1)
  .pipe(
     map((aList) => aList.map((v) => ({ 'number': v.code }))),
     map((listModified) => {
       return listModified?.length > 0
          ? listModified
          : list2.map((value) => ({ number: value.id }));
     })
   )
   .subscribe(console.log);

Don't get confused with map, one of them is from rxjs, the other is a function for arrays. In your first map, you are mapping the the whole array, not each element.

subscribe will be called when everything completes within the pipe.

like this ?

const first = []; // could be [{code: 1}, {code: 2}]
const second = [{id: 1}, {id: 2}];

of(first).pipe(
  filter(({length}) => length > 0),
  defaultIfEmpty(second),
  map((arr) => arr.map((x) => ({number: x.code ?? x.id})))
).subscribe(...);
Related