RxJava:flatMap中的if/else操作

3

我尝试获取并渲染以下类似数据:

原始数据类

  @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(在注释中)的语句写成一行(或更易读的方式),但是我不知道该怎么做。

defaultIfEmpty 运算符似乎在这种情况下无法使用。

有没有人有什么想法?

3个回答

11
这个怎么样:
    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(...)

0
据我所知,你不能使用一行代码来实现它。 你必须确定流是否为空,如果不为空,则再次发出项目。你可以使用缓存来避免重复计算。
 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());
            }
        });

但是,不一定必须在所有情况下都使用RxJava,有时候可以使用其他API来简化代码。例如,在您的情况下,您可以使用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);
                }
            }
        })

我看到答案是“不”,但cache运算符很有用。谢谢! - Yuki Yoshida

0

你可以使用 operator filter() 与 switchIfEmpty(...)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接