Reputation: 127
Say that we have a 3-dimensional List of Objects:
class OneDObject {
int id;
List<Integer> list;
OneDObject(int id, List<Integer>list) { /* Constructor */ }
// Getters and Setters
}
class TwoDObject {
int id;
List<OneDObject> list;
TwoDObject(int id, List<OneDObject> list) { /* Constructor */ }
// Getters and Setters
}
var l1 = List.of(1,2,4);
var l2 = List.of(2,4,6);
var obj1d1 = new OneDObject(1, l1);
var obj1d2 = new OneDObject(2, l2);
var l3 = List.of(obj1d1, obj1d2);
var l4 = List.of(obj1d1);
var obj2d1 = new TwoDObject(3, l3);
var obj2d2 = new TwoDObject(4, l4);
var l5 = List.of(obj2d1, obj2d2); // 3-d list
Say that I want to filter "l5" such that if any element in the inner most list is an odd number then the entire list should be deleted, and if that makes the 2nd level list as empty, then that should be deleted in return.
So, for the given example, before filtering if it is:
[[[1,2,4],[2,4,6]], [[1,2,4]]]
After filtering, it should be:
[[[2,4,6]]]
How can I do this using streams in Java?
Upvotes: 3
Views: 1326
Reputation: 21113
The following returns the desired result if you don't actually care about mutating the initial lists and keeping the same in-memory TwoDObject
objects you started with — i.e. if creating new filtered objects is acceptable.
List<TwoDObject> filtered = l5.stream()
.map(do2 -> new TwoDObject(do2.getId(), do2.getList().stream().filter(
do1 -> do1.getList().stream().noneMatch(e -> e % 2 == 1)).toList()))
.filter(do2 -> !do2.getList().isEmpty())
.toList();
This creates a new copied TwoDObject
value for each element of the list, but with its list
value filtered to just the OneDObject
values that contain no odd numbers. It then filters out any of these new TwoDObject
values with zero items remaining in its list (i.e. any TwoDObject
where all its OneDObject
elements were filtered out by the previous step).
Upvotes: 0
Reputation: 4935
Since you need your lists to be updated, in the below solution I am using removeIf
method of the List
to remove any elements which does not meet the necessary criteria. So for removeIf
to work, the list should not be immutable. So replace the var list = List.of(...)
code with var list = new ArrayList<>(List.of(...));
(Note: Null checks have been ignored as well.)
Now, this problem could be split into components:
Predicate<OneDObject> hasOdd = obj-> obj.getList().stream().anyMatch(i -> i % 2 != 0);
Predicate<TwoDObject> validate2d = obj -> {
// remove any 1d list that has atleast one odd number.
obj.getList().removeIf(hasOdd);
// check if there are any valid 1d lists
return obj.getList().isEmpty();
};
l5.removeIf(validate2d); // l5 will now contain only the 2d object having [2,4,6] list
Upvotes: 3
Reputation: 153
Here's the final code (in Java, but I think it should almost be interchangeable with Kotlin)
List<TwoDObject> l6 = l5.stream()
.peek(twoDObject -> {
List<OneDObject> filteredOneDObjectList = twoDObject.getList()
.stream()
.filter(oneDObject -> oneDObject.getList()
.stream()
.noneMatch(i -> i % 2 == 1))
.toList();
twoDObject.setList(filteredOneDObjectList);
})
.filter(twoDObject -> twoDObject.getList().size() > 0)
.toList();
First we go through every twoDObject
by calling Stream#peek
, then stream its list and filter out every oneDObject, which contains an odd number. Then the list is saved back into the current twoDObject.
In the end we filter out all empty twoDObjects.
Note that Stream#peek
should normally only be used for the purpose of debugging and not mutating the stream elements.
In this case it could also be replaced with
List<TwoDObject> l6 = l5.stream()
.map(twoDObject -> {
...
return twoDObject;
})
...
Upvotes: 1