Rx java RxJava-创建GroupByUntlChanged运算符?

Rx java RxJava-创建GroupByUntlChanged运算符?,rx-java,reactive-programming,Rx Java,Reactive Programming,我正在尝试创建自己的RxJava操作符,名为groupbyuntlchanged()。它的功能类似于一个groupBy(),但排放量被假定为基于键的顺序。因此,当键值更改时,它完成了GroupedObservable,并为下一个键转到下一个GroupedObservable 这是我到目前为止的工作。我使用每个字符串的第一个字母作为键。在我在末尾抛出一个“A”字符串之前,这似乎工作得很好 public class Test { public static void main(String[

我正在尝试创建自己的RxJava操作符,名为
groupbyuntlchanged()
。它的功能类似于一个
groupBy()
,但排放量被假定为基于键的顺序。因此,当键值更改时,它完成了
GroupedObservable
,并为下一个键转到下一个
GroupedObservable

这是我到目前为止的工作。我使用每个
字符串的第一个字母作为键。在我在末尾抛出一个“A”
字符串之前,这似乎工作得很好

public class Test {
    public static void main(String[] args) {
        Observable<String> items =
                Observable.just("Alpha","Adam","Apple","Beta","Brick","Bridge","Bat","Gamma","Gorilla","Axe");

        Func1<String,String> keyExtractor = s -> s.substring(0,1);

        items.compose(orderedGroupBy(keyExtractor))
                .flatMap(grp -> grp.toList())
                .subscribe(System.out::println);

    }

    public static <T,K> Observable.Transformer<T,GroupedObservable<K,T>> orderedGroupBy(Func1<T,K> keySelector) {
        return obs -> obs.groupBy(keySelector)
                .map(grp ->
                      GroupedObservable.from(grp.getKey(),grp.takeWhile(t -> keySelector.call(t).equals(grp.getKey())))
                );
    }
}
当我真的想要这个的时候:

[Alpha, Adam, Apple]
[Beta, Brick, Bridge, Bat]
[Gamma, Gorilla]
[Axe]

我该怎么做才能使一组有序的排放量在键改变时
onComplete()
GroupedObservable

协调
GroupedObservable
操作符的
groupBy
完成是一件非常棘手的事情(尽管在您的情况下同步处理可能会启用其他解决方案)。因此,
groupBy
有一个重载,允许您指定
mapFactory
。如果根据
groupBy
重载上的javadoc使用Guava
CacheBuilder
,则可以为地图和所需行为结果指定最大大小1:

Func1<String,String> keySelectory = s -> s.substring(0,1);
Func1<String,String> elementSelectory = s -> s;
Func1<Action1<String>, Map<String, String>> mapFactory =
   action -> 
     CacheBuilder.newBuilder()
       .maximumSize(1)
       .removalListener(notification ->
          action.call(notification.getKey()))
     .<String, String> build().asMap();
items.groupBy(keySelector, elementSelector, mapFactory)
            .flatMap(grp -> grp.toList())
            .subscribe(System.out::println);
Func1 keyselection=s->s.substring(0,1);
Func1 elementSelectory=s->s;
Func1映射工厂=
行动->
CacheBuilder.newBuilder()
.最大尺寸(1)
.removalListener(通知->
action.call(notification.getKey())
. build().asMap();
items.groupBy(keySelector、elementSelector、mapFactory)
.flatMap(grp->grp.toList())
.subscribe(System.out::println);

此类问题最好通过自定义运算符解决-依赖于状态(此处为已处理的项及其键)的转换不是反应式方法的最佳目标,通常需要
主题
。使用内置运算符,冷观测的(详细)解决方案如下:

public static <K, T> Observable.Transformer<T, GroupedObservable<K, T>> groupByUntilChanged(Func1<? super T, ? extends K> keyExtractor) {
    return observable -> groupByUntilChanged(observable, keyExtractor);
}

static <K, T> Observable<GroupedObservable<K, T>> groupByUntilChanged(Observable<T> itemsStream,
                                                                      Func1<? super T, ? extends K> keyExtractor) {

    /*keys according to keyExtractor */
    Observable<K> keysStream = itemsStream.distinctUntilChanged(keyExtractor).map(keyExtractor);
    Func1<K, Func1<T, Boolean>> itemByKey = key -> item -> key.equals(keyExtractor.call(item));

    /*predicate functions to match sub stream specified by key extractor*/
    Observable<Func1<T, Boolean>> itemsByKeyFuncStream = keysStream.map(itemByKey);

    /*stream chunks are processed sequentially, some kind of state bookkeeping is needed: let it be the number of
      already processed items */
    BehaviorSubject<Integer> skipCountStream = BehaviorSubject.create(0);

    Observable<GroupedObservable<K, T>> groupByUntilChangedStream = itemsByKeyFuncStream.concatMap(takeF ->

            /*skip already processed items, take while key extractor predicate is true*/
            skipCountStream.first().map(count -> itemsStream.skip(count).takeWhile(takeF)))

            .doOnNext(subItems ->
                    /*once group is ready, increase number of already processed items*/
                    subItems.count()
                            .flatMap(subItemsSize -> skipCountStream.first().map(allSize -> allSize + subItemsSize))
                            .subscribe(skipCountStream::onNext))
             /*convert to GroupedObservable*/
            .zipWith(keysStream, (obs, key) -> GroupedObservable.from(key, obs));

    return groupByUntilChangedStream;
}

刚刚意识到我上面的工作被破坏了。不过仍然需要一个解决方案。谷歌担心这很棘手。好的,让我来处理这些信息。。。我需要把番石榴的那部分弄得一团糟ya@DaveMoten修复编译错误后,代码执行结果是单一通知:[axe]——这与OP预期的结果相差甚远。我错过什么了吗?好的,哇。这让我心神不宁。我需要再研究一点,但这很聪明。我希望有一种方法可以避免使用
toList()
主题。但我认为这里有一些很棒的想法。让我玩一下,稍后我将标记为答案。
.toList().map(List::size)
零件应替换为
count()
。至于主题,我很有兴趣看到没有它们的解决方案
public static <K, T> Observable.Transformer<T, GroupedObservable<K, T>> groupByUntilChanged(Func1<? super T, ? extends K> keyExtractor) {
    return observable -> groupByUntilChanged(observable, keyExtractor);
}

static <K, T> Observable<GroupedObservable<K, T>> groupByUntilChanged(Observable<T> itemsStream,
                                                                      Func1<? super T, ? extends K> keyExtractor) {

    /*keys according to keyExtractor */
    Observable<K> keysStream = itemsStream.distinctUntilChanged(keyExtractor).map(keyExtractor);
    Func1<K, Func1<T, Boolean>> itemByKey = key -> item -> key.equals(keyExtractor.call(item));

    /*predicate functions to match sub stream specified by key extractor*/
    Observable<Func1<T, Boolean>> itemsByKeyFuncStream = keysStream.map(itemByKey);

    /*stream chunks are processed sequentially, some kind of state bookkeeping is needed: let it be the number of
      already processed items */
    BehaviorSubject<Integer> skipCountStream = BehaviorSubject.create(0);

    Observable<GroupedObservable<K, T>> groupByUntilChangedStream = itemsByKeyFuncStream.concatMap(takeF ->

            /*skip already processed items, take while key extractor predicate is true*/
            skipCountStream.first().map(count -> itemsStream.skip(count).takeWhile(takeF)))

            .doOnNext(subItems ->
                    /*once group is ready, increase number of already processed items*/
                    subItems.count()
                            .flatMap(subItemsSize -> skipCountStream.first().map(allSize -> allSize + subItemsSize))
                            .subscribe(skipCountStream::onNext))
             /*convert to GroupedObservable*/
            .zipWith(keysStream, (obs, key) -> GroupedObservable.from(key, obs));

    return groupByUntilChangedStream;
}
Observable<String> itemsStream =
            Observable.just("Alpha", "Adam", "Apple", "Beta", "Brick", "Bridge", "Bat", "Gamma", "Gorilla", "Axe");
    Func1<String, String> keyExtractor = s -> s.substring(0, 1);
    Observable<GroupedObservable<String, String>> groupByUntilChangedStream = itemsStream.compose(groupByUntilChanged(keyExtractor));

    /*groups starting with "A"*/
    groupByUntilChangedStream
            .filter(grouped -> grouped.getKey().equals("A"))
            .flatMap(Observable::toList)
            .defaultIfEmpty(Collections.emptyList())
            .subscribe(System.out::println);
[Alpha, Adam, Apple]
[Axe]