Logo

dev-resources.site

for different kinds of informations.

๐Ÿงน Improve Filtering with the Predicate Interface!

Published at
1/4/2025
Categories
android
kotlin
tutorial
learning
Author
nexy791
Categories
4 categories in total
android
open
kotlin
open
tutorial
open
learning
open
Author
7 person written this
nexy791
open
๐Ÿงน 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 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)
Enter fullscreen mode Exit fullscreen mode

๐Ÿ›‘ 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()
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿšฉ 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();
    }
}
Enter fullscreen mode Exit fullscreen mode

โ›“๏ธ 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
    }
}
Enter fullscreen mode Exit fullscreen mode

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) }
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ” 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)
    }
}
Enter fullscreen mode Exit fullscreen mode
fun filterByCategoryOrName(
    products: List<Product>,
    query: String,
): List<Product> {
    val predicate = ProductNamePredicate(query).or(ProductCategoryPredicate(query))
    return products.filter { predicate.test(it) }
}
Enter fullscreen mode Exit fullscreen mode

๐ŸŽ‰ 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! โญ

kotlin Article's
30 articles in total
Favicon
Using SVGs on Canvas with Compose Multiplatform
Favicon
Kotlin Generics Simplified
Favicon
Understanding Quick Sort in Kotlin : A Beginner's Guide
Favicon
Understanding Selection Sort in Kotlin: A Beginner's Guide
Favicon
Wednesday Links - Edition 2025-01-08
Favicon
Testing Pi Logic integrations.
Favicon
Understanding (a bit of) the Gradle Kotlin DSL
Favicon
Android TreeView(Hierarchy based) with Kotlin
Favicon
Creando un notebook con Jupyter y Kotlin
Favicon
Getting Started with Android Testing: Building Reliable Apps with Confidence (Part 3/3)
Favicon
Getting Started with Android Testing: Building Reliable Apps with Confidence (Part 2/3)
Favicon
Understanding Room Database in Android: A Beginner's Guide
Favicon
Fixing Rounded Navigation Bar Corner Padding in Jetpack Compose
Favicon
Getting Started with Android Testing: Building Reliable Apps with Confidence (Part 1/3)
Favicon
My conference year
Favicon
Authentication in Android Project with Firebase.
Favicon
Learn Firebase for Android Development from Scratch, a beginner guide.
Favicon
๐Ÿงน Improve Filtering with the Predicate Interface!
Favicon
How to make the best of a slow machine running on limited resources with a Windows environment as a Java Engineer
Favicon
How to implement detekt in Spring Boot + Kotlin + Gradle project
Favicon
How to Create and Publish an Android Library for Beginners
Favicon
Pub-sub Redis in Micronaut
Favicon
ISBN Stacks โ€” A look at a possible Spring Application implementation without annotations
Favicon
Protecting Applications with Kong security plugins and using StatsD to monitor system states โ€” A healthy camera story
Favicon
Configurable Kong API Gateway with Micronaut Services in Kotlin โ€” A very odd Yucca tribute concert
Favicon
Learning AWS with Localstack and Reactive Kotlin โ€” A stamps and coins implementation
Favicon
Coroutines, Distributed Cache, Resilience, and Replication in Kotlin โ€” Making a VMAโ€™s application
Favicon
From Paris to Berlin โ€” Creating Circuit-Breakers in Kotlin
Favicon
Understanding Merge Sort in Kotlin: A Beginner's Guide
Favicon
City Library โ€” An advanced guide to Circuit Breakers in Kotlin

Featured ones: