Skip to content

Commit

Permalink
[jOOQ#221]Add Agg.medianAll() and Agg.percentileAll()
Browse files Browse the repository at this point in the history
[jOOQ#221]Add Agg.medianAll() and Agg.percentileAll()
  • Loading branch information
TRebirthC committed May 31, 2020
1 parent 0a68e9a commit 07eba36
Show file tree
Hide file tree
Showing 2 changed files with 236 additions and 1 deletion.
204 changes: 204 additions & 0 deletions jOOL/src/main/java/org/jooq/lambda/Agg.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
*/
public class Agg {

private static double half = 0.5;
private static double zero = 0.0;
private static double one = 1.0;

/**
* Get a {@link Collector} that filters data passed to downstream collector.
*/
Expand Down Expand Up @@ -816,6 +820,86 @@ else if (l1[0] == null)
return percentileBy(0.5, function, comparator);
}

/**
* Get a {@link Collector} that calculates the derived <code>MEDIAN()</code> function given a natural ordering.
* <p>This method will return all the eligible elements if they exist.
* @param <T> The type of element
* @return A {@link Seq} of type <code>T</code>
*/
public static <T extends Comparable<? super T>> Collector<T, ?, Seq<T>> medianAll() {
return medianAllBy(t -> t, naturalOrder());
}

/**
* Get a {@link Collector} that calculates the derived <code>MEDIAN()</code> function given a specific ordering.
* <p>The <code>comparator</code> determines the order of original elements.
* @param <T> The type of element
* @param comparator The given comparator
* @return A {@link Seq} of type <code>T</code>
*/
public static <T> Collector<T, ?, Seq<T>> medianAll(final Comparator<? super T> comparator) {
return medianAllBy(t -> t, comparator);
}

/**
* Get a {@link Collector} that calculates the derived <code>MEDIAN()</code> function
* given a natural ordering and a function for elements.
* <p>This method is different with <code>medianAllBy</code> since this method will return the result
* after applying the <code>function</code>.
* @param <T> The type of element
* @param <U> The type of result of <code>function</code>
* @param function The given function
* @return A {@link Seq} of type <code>T</code>
*/
public static <T, U extends Comparable<? super U>> Collector<T, ?, Seq<U>> medianAll(final Function<? super T, ? extends U> function) {
return medianAll(function, naturalOrder());
}

/**
* Get a {@link Collector} that calculates the derived <code>MEDIAN()</code> function
* given a specific ordering and a function for elements.
* <p>This method is different with <code>medianAllBy</code> since this method will return the result
* after applying the <code>function</code>.
* @param <T> The type of element
* @param <U> The type of result of <code>function</code>
* @param function The given function
* @param comparator The given comparator
* @return A {@link Seq} of type <code>T</code>
*/
public static <T, U> Collector<T, ?, Seq<U>> medianAll(final Function<? super T, ? extends U> function, final Comparator<? super U> comparator) {
return collectingAndThen(medianAllBy(function, comparator), t -> t.map(function));
}

/**
* Get a {@link Collector} that calculates the derived <code>MEDIAN()</code> function
* given a natural ordering and a function for elements.
* <p>This method will sort the elements after applying the <code>function</code> based on
* <code>comparator</code> at first, then it will find the all eligible results.
* @param <T> The type of element
* @param <U> The type of result of <code>function</code>
* @param function The given function
* @return A {@link Seq} of type <code>T</code>
*/
public static <T, U extends Comparable<? super U>> Collector<T, ?, Seq<T>> medianAllBy(final Function<? super T, ? extends U> function) {
return medianAllBy(function, naturalOrder());
}

/**
* Get a {@link Collector} that calculates the derived <code>MEDIAN()</code> function
* given a specific ordering and a function for elements.
* <p>This method will sort the elements after applying the <code>function</code> based on
* <code>comparator</code> at first, then it will find the all eligible results.
* @param <T> The type of element
* @param <U> The type of result of <code>function</code>
* @param function The given function
* @param comparator The given comparator
* @return A {@link Seq} of type <code>T</code>
*/
public static <T, U> Collector<T, ?, Seq<T>> medianAllBy(final Function<? super T, ? extends U> function, final Comparator<? super U> comparator) {
return percentileAllBy(half, function, comparator);
}


/**
* Get a {@link Collector} that calculates the <code>PERCENTILE_DISC(percentile)</code> function given natural ordering.
*/
Expand Down Expand Up @@ -886,6 +970,126 @@ else if (percentile == 1.0)
}
);
}

/**
* Get a {@link Collector} that calculates the derived <code>PERCENTILE_DISC(percentile)</code> function
* given a natural ordering.
* <p>This method will return all the eligible elements if they exist.
* @param <T> The type of element
* @param percentile The percentile number
* @return A {@link Seq} of type <code>T</code>
*/
public static <T extends Comparable<? super T>> Collector<T, ?, Seq<T>> percentileAll(final double percentile) {
return percentileAllBy(percentile, t -> t, naturalOrder());
}

/**
* Get a {@link Collector} that calculates the derived <code>PERCENTILE_DISC(percentile)</code> function
* given a specific ordering.
* <p>The <code>comparator</code> determines the order of original elements.
* @param <T> The type of element
* @param percentile The percentile number
* @param comparator The given comparator
* @return A {@link Seq} of type <code>T</code>
*/
public static <T> Collector<T, ?, Seq<T>> percentileAll(final double percentile, final Comparator<? super T> comparator) {
return percentileAllBy(percentile, t -> t, comparator);
}

/**
* Get a {@link Collector} that calculates the derived <code>PERCENTILE_DISC(percentile)</code> function
* given a natural ordering.
* <p>This method is different with <code>percentileAllBy</code> since this method will return the result
* after applying the <code>function</code>.
* @param <T> The type of element
* @param <U> The type of result of <code>function</code>
* @param percentile The percentile number
* @param function The given function
* @return A {@link Seq} of type <code>T</code>
*/
public static <T, U extends Comparable<? super U>> Collector<T, ?, Seq<U>> percentileAll(final double percentile, final Function<? super T, ? extends U> function) {
return percentileAll(percentile, function, naturalOrder());
}

/**
* Get a {@link Collector} that calculates the derived <code>PERCENTILE_DISC(percentile)</code> function
* given a specific ordering.
* <p>This method is different with <code>percentileAllBy</code> since this method will return the result
* after applying the <code>function</code>.
* @param <T> The type of element
* @param <U> The type of result of <code>function</code>
* @param percentile The percentile number
* @param function The given function
* @param comparator The given comparator
* @return A {@link Seq} of type <code>T</code>
*/
public static <T, U> Collector<T, ?, Seq<U>> percentileAll(final double percentile, final Function<? super T, ? extends U> function, final Comparator<? super U> comparator) {
return collectingAndThen(percentileAllBy(percentile, function, comparator), t -> t.map(function));
}

/**
* Get a {@link Collector} that calculates the derived <code>PERCENTILE_DISC(percentile)</code> function
* given a natural ordering.
* <p>This method will sort the elements after applying the <code>function</code> based on
* <code>comparator</code> at first, then it will find the all eligible results.
* @param <T> The type of element
* @param <U> The type of result of <code>function</code>
* @param percentile The percentile number
* @param function The given function
* @return A {@link Seq} of type <code>T</code>
*/
public static <T, U extends Comparable<? super U>> Collector<T, ?, Seq<T>> percentileAllBy(final double percentile, final Function<? super T, ? extends U> function) {
return percentileAllBy(percentile, function, naturalOrder());
}

/**
* Get a {@link Collector} that calculates the derived <code>PERCENTILE_DISC(percentile)</code> function
* given a specific ordering.
* <p>This method will sort the elements after applying the <code>function</code> based on
* <code>comparator</code> at first, then it will find the all eligible results.
* @param <T> The type of element
* @param <U> The type of result of <code>function</code>
* @param percentile The percentile number
* @param function The given function
* @param comparator The given comparator
* @return A {@link Seq} of type <code>T</code>
* @throws IllegalArgumentException when the percentile is less than 0 or more than 1
*/
public static <T, U> Collector<T, ?, Seq<T>> percentileAllBy(final double percentile, final Function<? super T, ? extends U> function, final Comparator<? super U> comparator) {
if (percentile < 0.0 || percentile > 1.0) {
throw new IllegalArgumentException("Percentile must be between 0.0 and 1.0");
}

if (percentile == zero) {
return minAllBy(function, comparator);
} else if (percentile == one) {
return maxAllBy(function, comparator);
}

return Collector.of(
(Supplier<ArrayList<Tuple2<T, U>>>) ArrayList::new,
(l, v) -> l.add(tuple(v, function.apply(v))),
(l1, l2) -> {
l1.addAll(l2);
return l1;
},
l -> {
final int size = l.size();

if (size == zero) {
return Seq.empty();
} else if (size == one) {
return Seq.of(l.get(0).v1);
}

l.sort(Comparator.comparing(t -> t.v2, comparator));
// x.5 should be rounded down
final int index = (int) -Math.round(-(size * percentile + half)) - 1;
final T element = l.get(index).v1;
return Seq.seq(l.stream().filter(t -> t.v1.equals(element)).map(t -> t.v1));
}
);
}

/**
* Get a {@link Collector} that calculates the common prefix of a set of strings.
Expand Down
33 changes: 32 additions & 1 deletion jOOL/src/test/java/org/jooq/lambda/CollectorTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
import java.util.stream.Collector;
import java.util.stream.Stream;

import static java.util.Comparator.*;
import static org.jooq.lambda.Agg.*;
import static org.jooq.lambda.tuple.Tuple.tuple;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals

/**
* @author Lukas Eder
Expand Down Expand Up @@ -848,6 +849,36 @@ public void testMinMax() {
assertEquals(Optional.of(3), Seq.of(1, 2, 3).collect(minBy(i -> -i)));
assertEquals(Optional.of(1), Seq.of(1, 2, 3).collect(maxBy(i -> -i)));
}

@Test
public void testPercentileAll(){
assertEquals(List.of(), Seq.<Integer>of().collect(percentileAll(1.0)).toList());
assertEquals(List.of(1), Seq.of(1).collect(percentileAll(0.0)).toList());
assertEquals(List.of(3, 3), Seq.of(1, 3, 3, 2, 1, 2).collect(percentileAll(1.0)).toList());
assertEquals(List.of(2, 2), Seq.of(1, 1, 2, 2, 3, 3).collect(percentileAll(0.5)).toList());
assertEquals(List.of(1, 1), Seq.of(1, 1, 2, 2, 3, 3).collect(percentileAll(0.0)).toList());

assertEquals(List.of(-1, -1), Seq.of(1, 3, 3, 2, 1, 2).collect(percentileAll(1.0, t -> -t)).toList());
assertEquals(List.of(3, 3), Seq.of(1, 3, 3, 2, 1, 2).collect(percentileAll(1.0, comparingInt(o -> o))).toList());

assertEquals(List.of(4, 4), Seq.of(1, 3, 3, 2, 1, 2, 4, 4).collect(percentileAllBy(1.0, t -> t * t)).toList());
assertEquals(List.of(3, 3), Seq.of(1, 3, 3, 2, 1, 2, 4, 4).collect(percentileAllBy(0.75, t -> t * t)).toList());
assertEquals(List.of(2, 2), Seq.of(1, 3, 3, 2, 1, 2, 4, 4).collect(percentileAllBy(0.5, t -> t * t)).toList());
assertEquals(List.of(1, 1), Seq.of(1, 3, 3, 2, 1, 2, 4, 4).collect(percentileAllBy(0.25, t -> t * t)).toList());
assertEquals(List.of(1, 1), Seq.of(1, 3, 3, 2, 1, 2, 4, 4).collect(percentileAllBy(0.0, t -> t * t)).toList());

// Illegal args
Utils.assertThrows(IllegalArgumentException.class, () -> Stream.of("a").collect(percentileAllBy(-1, String::length)));
Utils.assertThrows(IllegalArgumentException.class, () -> Stream.of("a").collect(percentileAllBy(2, String::length)));
}

@Test
public void testMedianAll(){
assertEquals(List.of(2, 2), Seq.of(1, 3, 3, 2, 1, 2, 4, 4).collect(medianAll()).toList());
assertEquals(List.of(2, 2), Seq.of(1, 3, 3, 2, 1, 2, 4, 4).collect(medianAll(comparingInt(o -> o))).toList());
assertEquals(List.of(4, 4), Seq.of(1, 3, 3, 2, 1, 2, 4, 4).collect(medianAll(t -> t * t)).toList());
assertEquals(List.of(2, 2), Seq.of(1, 3, 3, 2, 1, 2, 4, 4).collect(medianAllBy(t -> t * t)).toList());
}

@Test
public void testAllAnyNone() {
Expand Down

0 comments on commit 07eba36

Please sign in to comment.