lundi 19 août 2019

What is preferred/proper way of filtering data? [directly in program code]

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.

Aucun commentaire:

Enregistrer un commentaire