RxJava:flatMap中的if / else操作

时间:2015-04-15 09:06:43

标签: java rx-java

我尝试获取并渲染一些数据,如下所示

原始数据类

  @Data @AllArgsConstructor class Category {
    String name;
    List<String> items;
  }

演示课

  @Data @AllArgsConstructor class ViewModel {
    public static final int TYPE_HEADER = 0;
    public static final int TYPE_ITEM = 1;
    int type;
    String category;
    String itemName;
  }

以下代码是请求并将订阅数据转换为表示对象。

  Observable.create(new Observable.OnSubscribe<Category>() {
      @Override public void call(Subscriber<? super Category> subscriber) {
        subscriber.onNext(new Category("1", Lists.newArrayList("", "a", "b")));
        subscriber.onNext(new Category("2", Lists.newArrayList("")));// this data does not output
        subscriber.onNext(new Category("3", Lists.newArrayList("c", "", "d")));
        subscriber.onNext(new Category("4", Lists.newArrayList("e", "f", "")));
      }
    }).flatMap(new Func1<Category, Observable<ViewModel>>() {
      @Override public Observable<ViewModel> call(Category category) {

        // TODO make this block to one line

        // 1. clean response data and transform to ViewModel    
        List<ViewModel> cleanedItems = Lists.newArrayList(
            Observable.from(category.getItems()).filter(new Func1<String, Boolean>() {
              @Override public Boolean call(String s) {
                return s != null && !s.isEmpty();
              }
            }).map(new Func1<String, ViewModel>() {
              @Override public ViewModel call(String item) {
                return new ViewModel(ViewModel.TYPE_ITEM, null, item);
              }
            }).toBlocking().toIterable());

        if (cleanedItems.isEmpty()) {
          // 2. case : skip
          return Observable.empty();
        } else {
          // 3. case : add header and cleaned data
          return Observable.concat(
              Observable.just(new ViewModel(ViewModel.TYPE_HEADER, category.getName(), null)),
              Observable.from(cleanedItems));
        }
      }
    }).subscribe(new Action1<ViewModel>() {
      @Override public void call(ViewModel viewModel) {
        // render data
        System.out.println(viewModel.toString());
      }
    });

输出

ViewModel(type=0, category=1, itemName=null)
ViewModel(type=1, category=null, itemName=a)
ViewModel(type=1, category=null, itemName=b)
ViewModel(type=0, category=3, itemName=null)
ViewModel(type=1, category=null, itemName=c)
ViewModel(type=1, category=null, itemName=d)
ViewModel(type=0, category=4, itemName=null)
ViewModel(type=1, category=null, itemName=e)
ViewModel(type=1, category=null, itemName=f)

我尝试将1,2,3,(注释)语句写入1行(或更易读的方式),但我不知道。 在这种情况下,defaultIfEmpty运算符似乎不会使用。

有人有任何想法吗?

3 个答案:

答案 0 :(得分:10)

这个怎么样:

    public static <T, R> Func1<? super T, ? extends Observable<? extends R>> ternary(
        Func1<T, Boolean> predicate,
        Func1<? super T, ? extends Observable<? extends R>> ifTrue,
        Func1<? super T, ? extends Observable<? extends R>> ifFalse) {
        return (item) -> predicate.call(item)
                ? ifTrue.call(item)
                : ifFalse.call(item);
    }

你可以这样使用它:

    .map(CharSequence::toString)
    .distinctUntilChanged()
    .flatMap(ternary(Strings::notNullOrEmpty,
        (kw) -> userRelationshipApi.searchFollowing(kw, null),
        (kw) -> Observable.just(selectedUserListAdapter.getUsers())))
    .subscribe(...)

答案 1 :(得分:0)

AFAIK你不能用一行来填充它。 您必须确定流是否为空,如果不再次发出项目。您可以使用缓存来避免重复计算。

 Observable.create(new Observable.OnSubscribe<Category>() {
            @Override
            public void call(Subscriber<? super Category> subscriber) {
                subscriber.onNext(new Category("1", Lists.newArrayList("", "a", "b")));
                subscriber.onNext(new Category("2", Lists.newArrayList("")));// this data does not output
                subscriber.onNext(new Category("3", Lists.newArrayList("c", "", "d")));
                subscriber.onNext(new Category("4", Lists.newArrayList("e", "f", "")));
                subscriber.onCompleted();
            }
        }).flatMap(new Func1<Category, Observable<ViewModel>>() {
            @Override
            public Observable<ViewModel> call(Category category) {

                final Observable<ViewModel> cleanedItems = Observable.from(category.getItems()).filter(new Func1<String, Boolean>() {
                    @Override
                    public Boolean call(String s) {
                        return s != null && !s.isEmpty();
                    }
                }).map(new Func1<String, ViewModel>() {
                    @Override
                    public ViewModel call(String item) {
                        return new ViewModel(ViewModel.TYPE_ITEM, null, item);
                    }
                }).cache();
                return cleanedItems.isEmpty().flatMap(new Func1<Boolean, Observable<ViewModel>>() {
                    @Override
                    public Observable<ViewModel> call(Boolean aBoolean) {
                        if (aBoolean) {
                            return Observable.empty();
                        } else {
                            return Observable.concat(
                            Observable.just(new ViewModel(ViewModel.TYPE_HEADER, category.getName(), null)),
                            cleanedItems);
                        }
                    }
                });
            }
        }).subscribe(new Action1<ViewModel>() {
            @Override
            public void call(ViewModel viewModel) {
                // render data
                System.out.println(viewModel.toString());
            }
        });

但是,有时您可以使用其他api来使代码更简单,而不是尽可能使用rxjava。在您的情况下,您可以使用例如stream api:

.flatMap(new Func1<Category, Observable<ViewModel>>() {
            @Override
            public Observable<ViewModel> call(Category category) {

                List<ViewModel> items = category.getItems().stream().filter(s -> s != null && !s.isEmpty()).map(s -> new ViewModel(ViewModel.TYPE_ITEM, null, s)).collect(Collectors.toList());
                if (items.isEmpty()) {
                    return Observable.empty();
                } else {
                    items.add(0, new ViewModel(ViewModel.TYPE_HEADER, category.getName(), null));
                    return Observable.from(items);
                }
            }
        })

答案 2 :(得分:0)

您可以将operator filter()与switchIfEmpty(...)

一起使用