The title may be a bit vague, but here is what I have (in privatized code):
A class with some fields, including a BigDecimal and Date:
class MyObj{
private java.math.BigDecimal percentage;
private java.util.Date date;
// Some more irrelevant fields
// Getters and Setters
}
In another class I have a list of these objects (i.e. java.util.List<MyObj> myList
). What I want now is a Java 8 stream to check if the list is in the correct order of both dates and percentages for my validator.
For example, the following list would be truthy:
[ MyObj { percentage = 25, date = 01-01-2018 },
MyObj { percentage = 50, date = 01-02-2018 },
MyObj { percentage = 100, date = 15-04-2019 } ]
But this list would be falsey because the percentage aren't in the correct order:
[ MyObj { percentage = 25, date = 01-01-2018 },
MyObj { percentage = 20, date = 01-02-2018 },
MyObj { percentage = 100, date = 15-04-2019 } ]
And this list would also be falsey because the dates aren't in the correct order:
[ MyObj { percentage = 25, date = 10-03-2018 },
MyObj { percentage = 50, date = 01-02-2018 },
MyObj { percentage = 100, date = 15-04-2019 } ]
One possible solution might be creating Pairs
like this and then using an !
and .anyMatch
checking each individual Pair<MyObj>
. But I don't really want to create a Pair
class just for this purpose if possible.
Is there perhaps a way to use .reduce
or something to loop over pairs of MyObj
to check them? What would be the best approach here to check if all dates and percentages of the MyObj
in my list are in the correct order using Java 8 stream?
Another possibility is perhaps sorting the list by date, and then checking if they are all in order of percentage, if that's easier than checking both fields are the same time. The same issue with comparing pairs of MyObj
for the percentage still remains, though.
(PS: I will use it for a com.vaadin.server.SerializablePredicate<MyObj> validator
, and I prefer a Java 8 lambda because I've also used some for the other validators, so it would be more in line with the rest of the code. The Java 8 lambda is more a preference than requirement in my question however.)
Well if you want a short-circuiting operation, I don't think an easy solution using stream-api exists... I propose a simpler one, first define a method that in a short-circuiting way will tell you if your List is sorted or not, based on some parameter:
private static <T, R extends Comparable<? super R>> boolean isSorted(List<T> list, Function<T, R> f) {
Comparator<T> comp = Comparator.comparing(f);
for (int i = 0; i < list.size() - 1; ++i) {
T left = list.get(i);
T right = list.get(i + 1);
if (comp.compare(left, right) >= 0) {
return false;
}
}
return true;
}
And calling it via:
System.out.println(
isSorted(myList, MyObj::getPercentage) &&
isSorted(myList, MyObj::getDate));
I think you are almost there by trying to use Stream.anyMatch
. You can accomplish it like this:
private static boolean isNotOrdered(List<MyObj> myList) {
return IntStream.range(1, myList.size()).anyMatch(i -> isNotOrdered(myList.get(i - 1), myList.get(i)));
}
private static boolean isNotOrdered(MyObj before, MyObj after) {
return before.getPercentage().compareTo(after.getPercentage()) > 0 ||
before.getDate().compareTo(after.getDate()) > 0;
}
We can use IntStream.range
to iterate over the elements of the list using an index. This way we can refer to any element in the list, e.g. the previous to compare it.
EDIT adding a more generic version:
private static boolean isNotOrderedAccordingTo(List<MyObj> myList, BiPredicate<MyObj, MyObj> predicate) {
return IntStream.range(1, myList.size()).anyMatch(i-> predicate.test(myList.get(i - 1), myList.get(i)));
}
This can be called as follows using the above predicate:
isNotOrderedAccordingTo(myList1, (before, after) -> isNotOrdered(before, after));
Or using method reference in a class ListNotOrdered
:
isNotOrderedAccordingTo(myList1, ListNotOrdered::isNotOrdered)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With