Reputation: 2089
I have multiple functions to filter a map of objects based on criteria.
Functions have the same body, only the boolean pass condition will change.
filterOnFirstCriteria(Criteria criterias, Map<Integer, List<Container>> containerMap, Map<Integer, ClassKey> classKeys) {
for (Map.Entry<Integer, List<Container>> entry : containerMap.entrySet()) {
List<Container> containers = entry.getValue();
ClassKey classKey = classKeys.get(entry.getKey());
for (Container container : containers) {
List<MyObject> found = new ArrayList<>();
for (MyObject myObject : container.getMyObjects()) {
boolean pass = criterias.getListOfFilter().stream().filter(s -> s.equals(container.getReference())).count() > 0;
if (pass) {
found.add(myObject);
}
}
container.getMyObjects().removeAll(found);
}
}
}
filterOnOtherCriteria(Criteria criterias, Map<Integer, List<Container>> containerMap, Map<Integer, ClassKey> classKeys) {
for (Map.Entry<Integer, List<Container>> entry : containerMap.entrySet()) {
List<Container> containers = entry.getValue();
ClassKey classKey = classKeys.get(entry.getKey());
for (Container container : containers) {
List<MyObject> found = new ArrayList<>();
for (MyObject myObject : container.getMyObjects()) {
boolean pass = myObject.getListOfObject().stream().filter(obj -> criterias.getLocations().stream().anyMatch(location -> location.equals(obj.getLocation()))).count() > 0;
if (pass) {
found.add(myObject);
}
}
container.getMyObjects().removeAll(found);
}
}
}
Conditions can be on any objects from upper loops. I would like to extract the duplicated loop code into a function but I don't know how to pass some kind of callback for the boolean condition.
Thank you.
Upvotes: 2
Views: 65
Reputation: 8078
The only difference in both methods are the statements:
boolean pass = criterias.getListOfFilter().stream()
.filter(s -> s.equals(container.getReference()))
.count() > 0;
and
boolean pass = myObject.getListOfObject().stream()
.filter(obj -> criterias.getLocations().stream()
.anyMatch(location -> location.equals(obj.getLocation())))
.count() > 0;
The first statement does not depend on myObject
fetched from the inner loop but on container
fetched from the outer loop. It depends as well on the parameter criterias
passed to the method.
The second statement depends on myObject
fetched from the inner loop and on the parameter criterias
passed to the method.
So if you want to refactor out those statements, which produce the boolean
, you have to find a unified method supporting both statements like:
@FunctionalInterface
public interface Guardian {
boolean pass(MyObject myObject, Container container);
}
Now you are ready to extract those two statements and pass them to your new method as lambdas:
public static void filterOnFirstCriteria(Criteria criterias, Map<Integer, List<Container>> containerMap, Map<Integer, ClassKey> classKeys) {
filterOn(criterias, containerMap, classKeys, (myObject, container) ->
criterias.getListOfFilter().stream()
.filter(s -> s.equals(container.getReference()))
.count() > 0);
}
public static void filterOnOtherCriteria(Criteria criterias, Map<Integer, List<Container>> containerMap, Map<Integer, ClassKey> classKeys) {
filterOn(criterias, containerMap, classKeys, (myObject, container) ->
myObject.getListOfObject().stream()
.filter(obj -> criterias.getLocations().stream().anyMatch(location -> location.equals(obj.getLocation())))
.count() > 0);
}
public static void filterOn(Criteria criterias, Map<Integer, List<Container>> containerMap, Map<Integer, ClassKey> classKeys, Guardian guardian) {
for (Map.Entry<Integer, List<Container>> entry : containerMap.entrySet()) {
List<Container> containers = entry.getValue();
ClassKey classKey = classKeys.get(entry.getKey());
for (Container container : containers) {
List<MyObject> found = new ArrayList<>();
for (MyObject myObject : container.getMyObjects()) {
if (guardian.pass(myObject, container)) {
found.add(myObject);
}
}
container.getMyObjects().removeAll(found);
}
}
}
Upvotes: 3