Search code examples
javajava-stream

How to combine the filters in streams to simplify


I have this following method that takes in a List of a CustomClass and performs filters using streams and returns at each step based on the result of the filter.

I was wondering if there was a way to simplify the code but combining the filters and statements together to make it more concise and efficient.

public String transform(List<CustomObject> listOfObjects) {

       listOfObjects = listOfObjects.stream()
            .filter(object -> object.objectType().equals("BUSINESS")).toList();

       // Primary check as all object should be of business type 
       // and if nothing exist we throw an exception
       if (listOfObjects.isEmpty()) {
           throw new RuntimeException("NO BUSINESS OBJECT FOUND");

       }

       // All objects are now of business type but we want them to be active
       List<CustomObject> listOfActiveObjects = listOfObjects.stream()
                     .filter(object -> object.objectStatus().equals("ACTIVE"))
                     .toList();

       // If no active object found just return the first business object sorted url

       if (listOfActiveObjects.isEmpty()) {
           return listOfObjects.stream()
                .sorted(Comparator.comparing(CustomObject::url))
                .toList().get(0).getUrl();
       }

       // Active objects are present so now filtered with proper locale

       List<CustomObject> listOfActiveObjectsWithLocale = listOfActiveObjects.stream()
                    .filter(object -> object.locale().equals("en-US"))
                    .toList();

       // If no locale was found just return the first sorted business active url

       if (listOfActiveObjectsWithLocale.isEmpty()) {
           return listOfActiveObjects.stream()
                 .sorted(Comparator.comparing(CustomObject::url))
                 .toList().get(0).getUrl();
       }

       // All filters applied, so within these objects return the sorted business/active/locale url
       return listOfActiveObjectsWithLocale.stream()
              .sorted(Comparator.comparing(CustomObject::url))
              .toList().get(0).getUrl();
   }

Solution

  • Check for Business objekts first then sort by your priority

    public static String transform(List<CustomObject> listOfObjects) {
    
        // Check if there are any "BUSINESS" objects in the list
        if (listOfObjects.stream().noneMatch(object -> object.objectType().equals("BUSINESS"))) {
            throw new RuntimeException("NO BUSINESS OBJECT FOUND");
        }
    
        // Sort the objects based on the desired criteria and return the first one
        return listOfObjects.stream()
            .sorted(Comparator
                .comparing((CustomObject object) -> !object.objectType().equals("BUSINESS")) // BUSINESS first
                .thenComparing(object -> !object.objectStatus().equals("ACTIVE")) // then ACTIVE
                .thenComparing(object -> !object.locale().equals("en-US")) // then locale en-US
                .thenComparing(CustomObject::getUrl) // finally by URL
            )
            .findFirst()
            .get()  // Since an element is guaranteed, we can safely call get()
            .getUrl();
    }
    

    Edit

    The now deleted comment was refering to calling Optional.get() Since in the if condition we made sure that there is at least one Business object, I don't think it is bad. But if you prefer you can change it to something like

    public String transform(List<CustomObject> listOfObjects) {
    
    if (listOfObjects.stream().noneMatch(object -> object.objectType().equals("BUSINESS"))) {
        throw new RuntimeException("NO BUSINESS OBJECT FOUND");
    }
    
    return listOfObjects.stream()
        .sorted(Comparator
            .comparing((CustomObject object) -> !object.objectType().equals("BUSINESS"))
            .thenComparing(object -> !object.objectStatus().equals("ACTIVE"))
            .thenComparing(object -> !object.locale().equals("en-US"))
            .thenComparing(CustomObject::url)
        )
        .findFirst()
        .map(CustomObject::getUrl)
        .orElseThrow(() -> new RuntimeException("Unexpected error during transformation"));
    }