Let's assume that you need to filter customers based on 3 criteria:
- name
- tags assigned to customer
- customer's location
For some reasons you have to do it directly in your program code, without database queries or PL/SQL for that matter.
Which of these two solutions do you find better in any terms? (e.g code readability, performance, cleanliness)
First example
public List<Customer> filterStepbyStep(List<Customer> customers,
String searchString,
List<Tag> selectedTags,
List<LocationId> selectedLocations) {
List<Customer> filteredCustomers = new ArrayList<Customer>(customers);
boolean searchStringNotEmpty = searchString != null && !searchString.trim().isEmpty();
boolean locationsNotEmpty = selectedLocations != null && selectedLocations.size() > 0;
boolean selectedTagsNotEmpty = selectedTags != null && selectedTags.size() > 0;
filteredCustomers.removeIf(customer -> {
boolean valid = true;
//searchString
if (valid && searchStringNotEmpty) {
valid = customer.name.contains(searchString);
}
//locations
if (valid && locationsNotEmpty) {
valid = selectedLocations.stream().anyMatch(locationId -> locationId.equals(customer.locationId));
}
//tags
if (valid && selectedTagsNotEmpty) {
boolean tagValid = false;
for (Tag selectedTag : selectedTags) {
for (Tag customerTag : customer.getTags()) {
if (selectedTag == customerTag) {
tagValid = true;
break;
}
}
if (tagValid) {
break;
}
}
valid = tagValid;
}
return !valid;
});
return filteredCustomers;
}
Second example
public List<Customer> filterWithLambda(List<Customer> customers,
String searchString,
List<Tag> selectedTags,
List<LocationId> selectedLocations) {
List<Customer> filteredCustomers = new ArrayList<Customer>(customers);
boolean searchStringNotEmpty = searchString != null && !searchString.trim().isEmpty();
boolean locationsNotEmpty = selectedLocations != null && selectedLocations.size() > 0;
boolean selectedTagsNotEmpty = selectedTags != null && selectedTags.size() > 0;
filteredCustomers.removeIf(customer -> {
return (searchStringNotEmpty && !customer.name.contains(searchString))
|| (locationsNotEmpty && !selectedLocations.stream().anyMatch(locationId -> locationId.equals(customer.locationId)))
|| (selectedTagsNotEmpty && !selectedTags.stream().anyMatch(selectedTag ->
customer.getTags().stream().anyMatch(customerTag ->
selectedTag == customerTag)));
});
return filteredCustomers;
}
For me second example is more readable, I can immediately see that customer gets filtered out (removed) if any of 3 operands of OR operator is true.
On the other hand repeating if(valid) for each property in first example does not seem too clean to me. I also find it tiresome and tedious to follow valid variable. Having this kind of state with valid variable is IMO error-prone.
The only reason I can see, that one would prefer first example is not being familiar with lambda expressions.
I've created small repl.it to try these approaches out.
If you can think of better way of doing this, than two above examples, then please feel free to post it here.