/** * Copyright 2013 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package rx.operators; import java.util.concurrent.ConcurrentHashMap; import rx.Observable; import rx.Observer; import rx.Subscription; import rx.observables.GroupedObservable; import rx.util.functions.Func1; import rx.util.functions.Functions; public final class OperatorGroupBy { public static Func1>, Subscription> groupBy(Observable source, final Func1 keySelector, final Func1 elementSelector) { final Observable> keyval = source.map(new Func1>() { @Override public KeyValue call(T t) { K key = keySelector.call(t); R value = elementSelector.call(t); return new KeyValue(key, value); } }); return new GroupBy(keyval); } public static Func1>, Subscription> groupBy(Observable source, final Func1 keySelector) { return groupBy(source, keySelector, Functions. identity()); } private static class GroupBy implements Func1>, Subscription> { private final Observable> source; private final ConcurrentHashMap keys = new ConcurrentHashMap(); private GroupBy(Observable> source) { this.source = source; } @Override public Subscription call(final Observer> observer) { return source.subscribe(new Observer>() { @Override public void onCompleted() { observer.onCompleted(); } @Override public void onError(Exception e) { observer.onError(e); } @Override public void onNext(final KeyValue args) { K key = args.key; boolean newGroup = keys.putIfAbsent(key, true) == null; if (newGroup) { observer.onNext(buildObservableFor(source, key)); } } }); } } private static GroupedObservable buildObservableFor(Observable> source, final K key) { final Observable observable = source.filter(new Func1, Boolean>() { @Override public Boolean call(KeyValue pair) { return key.equals(pair.key); } }).map(new Func1, R>() { @Override public R call(KeyValue pair) { return pair.value; } }); return new GroupedObservable(key, new Func1, Subscription>() { @Override public Subscription call(Observer observer) { return observable.subscribe(observer); } }); } private static class KeyValue { private final K key; private final V value; private KeyValue(K key, V value) { this.key = key; this.value = value; } } }