🧹 Improve Filtering with the Predicate Interface!

Working with lists in apps often means filtering data before showing it to users.

But, we know that filters can sometimes create complex and tricky code to maintain 😵‍💫

Today, we’ll dive into a cleaner, more reusable approach using the Predicate inte…


This content originally appeared on DEV Community and was authored by Oleksandr Yasinskyi

Working with lists in apps often means filtering data before showing it to users.

But, we know that filters can sometimes create complex and tricky code to maintain 😵‍💫

Today, we’ll dive into a cleaner, more reusable approach using the Predicate interface 🚀

📦 Let's imagine we're working on a fun app to help keep track of all our products in storage. We’ve got a Product model.

data class Product(val id: Int, val name: String, val price: Int, val inStock: Boolean, val weight: Int, val category: String)

🛑 The Problems with Traditional Filtering

Take a look at this filtering example:

fun filter(
    products: List<Product>,
    name: String?,
    price: String?,
    inStock: Boolean?,
    weight: Int?,
    category: String?
): List<Product> {
    return products
        .asSequence()
        .filter { product ->
            name?.let { product.name.contains(it, ignoreCase = true) } ?: true
        }
        .filter { product ->
            price?.let { product.price == it.toInt() } ?: true
        }
        .filter { product ->
            inStock?.let { product.inStock == it } ?: true
        }
        .filter { product ->
            weight?.let { product.weight == it } ?: true
        }
        .filter { product ->
            category?.let { product.category == it } ?: true
        }
        .toList()
}

🚩 Problems with this strategy:

  1. Code is hard to maintain:
    • Adding new conditions requires modifying existing code
  2. Poor readability:
    • Multiple filters that are difficult to understand and extend.
  3. Code is hard to reuse:
    • We have to duplicate the code if we want to use it in another context.

✅ Improving Validation with the Predicate Interface

The Predicate interface from java.util.function is here to save the day! It allows us to encapsulate individual validation rules and chain them together easily.

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
        ? Objects::isNull
        : object -> targetRef.equals(object);
    }

    static <T> Predicate<T> not(Predicate<? super T> target) {
        Objects.requireNonNull(target);
        return (Predicate<T>)target.negate();
    }
}

⛓️ What is the Chain of Responsibility Pattern?

The Chain of Responsibility pattern allows passing requests along a chain of handlers. Each handler decides whether to process the request or pass it to the next handler.

✨ Predicate in Action

Now, we will create predicates for each field of the Product model, such as category, weight, inStock, and so on. These predicates will check if values meet the given conditions. Each of these predicates will implement the Predicate<Product> interface, and we can combine them using the and, or, and negate methods.


class ProductWeightPredicate(private val weight: Int) : Predicate<Product> {
    override fun test(product: Product): Boolean {
        return product.weight == weight
    }
}

class ProductCategoryPredicate(private val category: String) : Predicate<Product> {
    override fun test(product: Product): Boolean {
        return product.category == category
    }
}

Now we can dynamically combine these predicates. For example:


private val predicates: MutableSet<Predicate<Product>> = mutableSetOf()


fun onWeightChanged(weight: Int) {
    predicates.add(ProductWeightPredicate(weight))
}

fun onCategoryChanged(category: String) {
    predicates.add(ProductCategoryPredicate(category))
}

fun onFilterClicked(products: List<Product>): List<Product> {
    val predicate = predicates.reduce { acc, predicate -> acc.and(predicate) }
    return products.filter { predicate.test(it) }
}

🔍 Searching by Category or Name

We can also combine predicates into different chains. For example, filtering by category or product name when searching:

class ProductNamePredicate(private val name: String) : Predicate<Product> {
    override fun test(product: Product): Boolean {
        return product.name.contains(name, ignoreCase = true)
    }
}
fun filterByCategoryOrName(
    products: List<Product>,
    query: String,
): List<Product> {
    val predicate = ProductNamePredicate(query).or(ProductCategoryPredicate(query))
    return products.filter { predicate.test(it) }
}

🎉 Benefits of This Approach

  1. Flexibility: We can add new filtering rules without modifying existing code. Each new condition is a new predicate.
  2. Reusability: Predicates can be reused in different contexts, greatly simplifying code maintenance.
  3. Clean Code: Your filtering logic becomes easier to read, extend, and maintain.

Happy coding! ⭐


This content originally appeared on DEV Community and was authored by Oleksandr Yasinskyi


Print Share Comment Cite Upload Translate Updates
APA

Oleksandr Yasinskyi | Sciencx (2025-01-04T00:47:03+00:00) 🧹 Improve Filtering with the Predicate Interface!. Retrieved from https://www.scien.cx/2025/01/04/%f0%9f%a7%b9-improve-filtering-with-the-predicate-interface/

MLA
" » 🧹 Improve Filtering with the Predicate Interface!." Oleksandr Yasinskyi | Sciencx - Saturday January 4, 2025, https://www.scien.cx/2025/01/04/%f0%9f%a7%b9-improve-filtering-with-the-predicate-interface/
HARVARD
Oleksandr Yasinskyi | Sciencx Saturday January 4, 2025 » 🧹 Improve Filtering with the Predicate Interface!., viewed ,<https://www.scien.cx/2025/01/04/%f0%9f%a7%b9-improve-filtering-with-the-predicate-interface/>
VANCOUVER
Oleksandr Yasinskyi | Sciencx - » 🧹 Improve Filtering with the Predicate Interface!. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/01/04/%f0%9f%a7%b9-improve-filtering-with-the-predicate-interface/
CHICAGO
" » 🧹 Improve Filtering with the Predicate Interface!." Oleksandr Yasinskyi | Sciencx - Accessed . https://www.scien.cx/2025/01/04/%f0%9f%a7%b9-improve-filtering-with-the-predicate-interface/
IEEE
" » 🧹 Improve Filtering with the Predicate Interface!." Oleksandr Yasinskyi | Sciencx [Online]. Available: https://www.scien.cx/2025/01/04/%f0%9f%a7%b9-improve-filtering-with-the-predicate-interface/. [Accessed: ]
rf:citation
» 🧹 Improve Filtering with the Predicate Interface! | Oleksandr Yasinskyi | Sciencx | https://www.scien.cx/2025/01/04/%f0%9f%a7%b9-improve-filtering-with-the-predicate-interface/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.