package tc.oc.commons.core.stream;
import java.util.Comparator;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.google.common.collect.Maps;
/**
* A {@link Stream} of {@link Map.Entry}s with extra methods to operate
* on the keys or values individually. This tends to be more readable
* than calling getKey() and getValue() all over the place.
*/
public interface BiStream<K, V> extends Stream<Map.Entry<K, V>> {
static <K, V> BiStream<K, V> from(Stream<Map.Entry<K, V>> entries) {
return new BiStreamImpl<>(entries);
}
static <K, V> BiStream<K, V> from(Map<K, V> map) {
return from(map.entrySet().stream());
}
static <K, V> BiStream<K, V> fromKeys(Stream<K> keys, Function<? super K, V> mapper) {
return from(keys.map(key -> Maps.immutableEntry(key, mapper.apply(key))));
}
static <K, V> BiStream<K, V> fromValues(Stream<V> values, Function<? super V, K> mapper) {
return from(values.map(value -> Maps.immutableEntry(mapper.apply(value), value)));
}
static <K, V> BiStream<K, V> empty() {
return from(Stream.empty());
}
static <K, V> BiStream<K, V> of(K key, V value) {
return from(Stream.of(Maps.immutableEntry(key, value)));
}
default <T> Stream<T> merge(BiFunction<? super K, ? super V, ? extends T> mapper) {
return map(entry -> mapper.apply(entry.getKey(), entry.getValue()));
}
default <T> Stream<T> flatMerge(BiFunction<? super K, ? super V, ? extends Stream<T>> mapper) {
return flatMap(entry -> mapper.apply(entry.getKey(), entry.getValue()));
}
default Stream<K> keys() {
return map(Map.Entry::getKey);
}
default Stream<V> values() {
return map(Map.Entry::getValue);
}
default BiStream<K, V> filter(BiPredicate<K, V> condition) {
return from(filter(entry -> condition.test(entry.getKey(), entry.getValue())));
}
default BiStream<K, V> filterKeys(Predicate<K> condition) {
return from(filter(entry -> condition.test(entry.getKey())));
}
default BiStream<K, V> filterValues(Predicate<V> condition) {
return from(filter(entry -> condition.test(entry.getValue())));
}
default <RK, RV> BiStream<RK, RV> map(BiFunction<? super K, ? super V, ? extends Map.Entry<RK, RV>> mapper) {
return from(map(entry -> mapper.apply(entry.getKey(), entry.getValue())));
}
default <RK, RV> BiStream<RK, RV> map(Function<? super K, ? extends RK> keyMapper, Function<? super V, ? extends RV> valueMapper) {
return from(map(entry -> Maps.immutableEntry(keyMapper.apply(entry.getKey()),
valueMapper.apply(entry.getValue()))));
}
default <RK, RV> BiStream<RK, RV> map(BiFunction<? super K, ? super V, ? extends RK> keyMapper, BiFunction<? super K, ? super V, ? extends RV> valueMapper) {
return from(map(entry -> Maps.immutableEntry(keyMapper.apply(entry.getKey(), entry.getValue()),
valueMapper.apply(entry.getKey(), entry.getValue()))));
}
default <R> BiStream<R, V> mapKeys(Function<? super K, ? extends R> mapper) {
return from(map(entry -> Maps.immutableEntry(mapper.apply(entry.getKey()),
entry.getValue())));
}
default <R> BiStream<K, R> mapValues(Function<? super V, ? extends R> mapper) {
return from(map(entry -> Maps.immutableEntry(entry.getKey(),
mapper.apply(entry.getValue()))));
}
default <RK, RV> BiStream<RK, RV> flatMap(BiFunction<? super K, ? super V, ? extends Stream<Map.Entry<RK, RV>>> mapper) {
return from(flatMap(entry -> mapper.apply(entry.getKey(), entry.getValue())));
}
default Optional<Map.Entry<K, V>> maxByKey(Comparator<? super K> comparator) {
return max((a, b) -> comparator.compare(a.getKey(), b.getKey()));
}
default Optional<Map.Entry<K, V>> maxByValue(Comparator<? super V> comparator) {
return max((a, b) -> comparator.compare(a.getValue(), b.getValue()));
}
default Optional<Map.Entry<K, V>> minByKey(Comparator<? super K> comparator) {
return min((a, b) -> comparator.compare(a.getKey(), b.getKey()));
}
default Optional<Map.Entry<K, V>> minByValue(Comparator<? super V> comparator) {
return min((a, b) -> comparator.compare(a.getValue(), b.getValue()));
}
}