12

How do you write a function that takes an Observable<T[]> and returns Observable<T>?

For example, I have two methods:

getTransactions(): Observable<Transaction[]>{
        ...
    }
getTransaction(id: number): Observable<Transaction>{
    return this.getTransactions().pipe(find(txn => txn.id === id));
}

I get the error that type 'Observable' is not assignable to type 'Observable'. Type 'Transaction[] is not assignable to type 'Transaction'. Property 'id' is missing in type 'Transaction[]'.

As I understand, the observable pipe functions (map, single, find, max, etc.) pertain to a stream of data (i.e., while the observable is emitting multiple items over time) and are not useful when the observable emits a single item (which happens to be an array) at once.

What is the proper way to write the getTransaction(id) function? I tried:

let transactions : Transaction[] = [];
this.getTransactions().subscribe(txns => transactions=txns);
return of(transactions.find(txn => txn.id === id));

But this causes an error saying 'transaction' is undefined. I'm very new to Angular etc. so any instruction is helpful. Thank you!

Stackblitz

I'm using:
Angular CLI: 6.0.3
Angular: 6.0.1
rxjs: 6.1.0

1

1 Answer 1

26

You need to be treating the observable value as though it is a stream of arrays, rather than attempting to treat it like a stream of streams of items. The values in your observable are single points of data that each contain their own array.

getTransaction(id: number): Observable<Transaction>{
    return this.getTransactions().pipe(
        map(txs => txs.find(txn => txn.id === id))
    );
}

What you're doing here is mapping each array in your stream into whatever item has an ID that matches the parameter.

4
  • 1
    Thank you, that definitely answered the question I asked. The problem I now have is that the mapping is creating a new object (I believe). Is there a way to have the getTransaction return an object that is an element of the array? So that if I modify the element, it is reflected in the array as well? As demonstrated here: stackblitz.com/edit/angular-stackblitz-demo-capqcs Commented May 21, 2018 at 19:12
  • 2
    find should just return the object, not a copy. However, it's bad practice to rely on object mutation by reference in general, and leads to hard to understand and maintain code. It's preferable to never mutate and always explicitly update.
    – bryan60
    Commented May 21, 2018 at 19:23
  • Thank you, that makes sense. I'm a little uncertain what you mean by 'explicitly update' and 'mutate'. Do you mean that I should leave the Observable<Transaction[]> alone and if I want to get a single transaction, I should let the caller methods deal with the array? Or I should set up a data manager that holds the data and generates observables off of the data and processes updates to the observables into the data? Is this best practice? (ngfor, ngif to select the transaction of interest) stackblitz.com/edit/angular-stackblitz-demo-r4ibzd Commented May 21, 2018 at 19:59
  • 1
    if you want to change the array, you should change a copy of it and pass it back through the source
    – bryan60
    Commented May 21, 2018 at 20:09

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.