Search code examples
flutterdartrxdart

How to map each item from observable to another one that comes from async function?


I want to

1.map item from observable to another one if it has already saved in database.

2.otherwise, use it as it is.

and keep their order in result.

Saved item has some property like tag, and item from observable is 'raw', it doesn't have any property.

I wrote code like this and run testMethod.

    class Item {
      final String key;
      String tag;
      Item(this.key);
      @override
      String toString() {
        return ('key:$key,tag:$tag');
      }
    }

    class Sample {
    ///this will generate observable with 'raw' items.
    static Observable<Item> getItems() {
        return Observable.range(1, 5).map((index) => Item(index.toString()));
    }

    ///this will find saved item from repository if it exists.
    static Future<Item> findItemByKey(String key) async {
        //simulate database search
        await Future.delayed(Duration(seconds: 1));
        if (key == '1' || key == '4') {
            final item = Item(key)..tag = 'saved';
            return item;
        } else
            return null;
    }

    static void testMethod() {
        getItems().map((item) async {
            final savedItem = await findItemByKey(item.key);
            if (savedItem == null) {
                print('not saved:$item');
                return item;
            } else {
                print('saved:$savedItem');
                return savedItem;
            }
        }).listen((item) {});
    }

The result is not expected one.

expected:

saved:key:1,tag:saved
not saved:key:2,tag:null
not saved:key:3,tag:null
saved:key:4,tag:saved
not saved:key:5,tag:null

actual:

not saved:key:2,tag:null
not saved:key:3,tag:null
not saved:key:5,tag:null
saved:key:1,tag:saved
saved:key:4,tag:saved

How to keep their order in result?


Solution

  • I answer myself to close this question.

    According to pskink's comment, use asyncMap or concatMap solve my problem. Thanks!!

    below is new implementation of testMethod.

    asyncMap version:

    getItems().asyncMap((item) {
      final savedItem = findItemByKey(item.key);
      if (savedItem != null)
        return savedItem;
      else
        return Future.value(item);
    }).listen(print);
    

    concatMap version:

    getItems().concatMap((item) {
      final savedItem = findItemByKey(item.key);
      if (savedItem != null)
        return Observable.fromFuture(savedItem);
      else
        return Observable.just(item);
    }).listen(print);