I have a list of objects that looks like this:
class Test {
String a;
int sortKey;
}
List<Test> testList;
This testList
is sorted based on sortKey
.
I want to find the first Test
object in the testList
where the value of a
is "X"
and the value of all previous Test
objects in the list till this element is found is "Y"
This is what I am trying
testList
.stream()
.sorted(sortBasedOnSortKey())
.filter(test -> test.a().equals("X"))
.findFirst();
How to make sure the previous elements have a value of a == "Y"
?
CodePudding user response:
- Sort the list before the stream.
- Using
IntStream
, find the index of the first element matching the filter"X".equals(a)
- If such index is found, check that all elements before the index match another filter
"Y".equals(a)
static Test findFirstAfterAll(String first, String all, List<Test> testList) {
testList.sort(Comparator.comparingInt(Test::getSortKey));
int index = IntStream
.range(0, testList.size())
.filter(i -> first.equals(testList.get(i).getA()))
.findFirst()
.orElse(-1);
return IntStream.range(0, index)
.allMatch(i -> all.equals(testList.get(i).getA()))
? testList.get(index) : null;
}
Test:
List<Test> testList = Arrays.asList(
new Test("X", 20),
new Test("Y", 15),
new Test("Y", 12)
);
System.out.println(findFirstAfterAll("X", "Y", testList));
Output:
Test(a=X, sortKey=20)
Using Java 9 Stream::takeWhile
could work too assuming that no element with a.equals("Y")
may appear before "X":
static Optional<Test> findFirstAfterAll(String first, String all, List<Test> testList) {
return testList.stream()
.sorted(Comparator.comparingInt(Test::getSortKey))
.takeWhile(t -> all.equals(t.getA()) || first.equals(t.getA()))
.filter(t -> first.equals(t.getA()))
.findFirst();
}
Tests:
System.out.println(findFirstAfterAll("X", "Y", Arrays.asList(new Test("Y", 20), new Test("X", 15), new Test("X", 12))));
System.out.println(findFirstAfterAll("X", "Y", Arrays.asList(new Test("X", 20), new Test("Y", 15), new Test("Y", 12))));
System.out.println(findFirstAfterAll("X", "Y", Arrays.asList(new Test("Y", 20), new Test("Y", 15), new Test("Y", 12))));
System.out.println(findFirstAfterAll("X", "Y", Arrays.asList(new Test("Y", 20), new Test("X", 15), new Test("A", 12))));
Output:
Optional[Test{a='X', sortKey=12}]
Optional[Test{a='X', sortKey=20}]
Optional.empty
Optional.empty
The logic is that only "X" or "Y" may appear in the beginning of the sorted list and as soon as "X" is found it's ok.
If at least one "Y" MUST appear before X to make a valid match, similar solution using takeWhile
can be implemented:
static Optional<Test> findFirstAfterAtLeastOne(String first, String all, List<Test> testList) {
testList.sort(Comparator.comparingInt(Test::getSortKey));
int index = (int) IntStream.range(0, testList.size())
.takeWhile(i -> all.equals(testList.get(i).getA()))
.count();
return index == 0
? Optional.empty()
: IntStream.range(index, testList.size())
.takeWhile(i -> first.equals(testList.get(i).getA()))
.mapToObj(testList::get)
.findFirst();
}
Here the output for the same test data is:
Optional.empty // no Y before X
Optional[Test{a='X', sortKey=20}]
Optional.empty
Optional.empty
CodePudding user response:
Well, you can combine takeWhile
and dropWhile
:
Test[] tests = {
new Test("Y", 1),
new Test("Y", 2),
new Test("Y", 3),
new Test("X", 4), // Expecting to yield this value
new Test("O", 5),
new Test("X", 6),
new Test("X", 7)
};
Optional<Test> test = Stream.of(tests)
.takeWhile(t -> t.a().equals("X") || t.a().equals("Y"))
.dropWhile(t -> t.a().equals("Y"))
.findFirst();