Rx java RxJava-创建GroupByUntlChanged运算符?
我正在尝试创建自己的RxJava操作符,名为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[
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使用GuavaCacheBuilder
,则可以为地图和所需行为结果指定最大大小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]