hkn
hkn

Reputation: 371

Filtering list with multi criterias

I have a class named Data; i have a list with some data and a function that filters this list with properties of data class

if a,b,c  filter words are not empty => data object must have these 3
if a,b are not empty => data object must have these 2
if a,c are not empty => data object must have these 2
if b,c are not empty => data object must have these 2
if a is not empty    => data object must have this
if b is not empty    => data object must have this
if c is not empty    => data object must have this

as you can see i wrote a code that fulfills its job but i did not like my algorithm, so can you suggest me more smart algorithm because if field count of class increase i need to wrote new ifs. (i am using java but i will not add java tag maybe there is a good pseudo code but javas functions are accepted too )

public class Data {

    private String a;
    private String b;
    private String c;

    private String getA() {
        return a;
    }

    private String getB() {
        return b;
    }

    private String getC() {
        return c;
    }


    private List<Data> filterData(List<Data> datas, String a, String b, String c) {
        List<Data> result = new ArrayList<>();

        for (Data data : datas) {
            if (a != null && data.getA().equals(a) && b != null && data.getB().equals(b) && c != null && data.getC().equals(c)) {
                result.add(data);
            } else if (a != null && data.getA().equals(a) && b != null && data.getB().equals(b)) {
                result.add(data);
            } else if (a != null && data.getA().equals(a) && c != null && data.getC().equals(c)) {
                result.add(data);
            } else if (b != null && data.getB().equals(b) && c != null && data.getC().equals(c)) {
                result.add(data);
            } else if (a != null && data.getA().equals(a)) {
                result.add(data);
            } else if (b != null && data.getB().equals(b)) {
                result.add(data);
            } else if (c != null && data.getC().equals(c)) {
                result.add(data);
            } else {
                return null;
            }
        }
        return result;
    }
}

assume that you have cars list and Car class has color (a), model year (b), fuel type (c) assume that they are string not enum or you dont need to compare model year from its range

Upvotes: 0

Views: 80

Answers (3)

tobias_k
tobias_k

Reputation: 82929

You can drastically reduce the number of if checks if you transform your condition. Currently, your condition (split up over 7 ifchecks), looks roughly like this:

(a & b & c) | (b & c) | (a & c) | (a & b) | a | b | c

Where each of a, b and c corresponds to x != null && data.getX().equals(x)

You can simplify this to just a & b & c if you change those components to x == null || data.getX().equals(x), i.e. either the element is null, or it matches. You could also make this a separate method to make your code clearer. Also, you could replace the loop and if checks with Stream.filter

    private boolean matches(Object x, Object y) {
        return x == null || x.equals(y);
    }

    private List<Data> filterData(List<Data> datas, String a, String b, String c) {
        return datas.stream()
                .filter(d -> matches(a, d.getA()))
                .filter(d -> matches(b, d.getB()))
                .filter(d -> matches(c, d.getC()))
                .collect(Collectors.toList());
    }

Note: 1) I changed data.getX().equals(x) to x.equals(data.getX()), as you already know that x is != null at this point, but getX() might be null. Also, your final if check, if all the filters are null, added null to the list, instead of data. I assumed this was a mistake.

Upvotes: 1

btilly
btilly

Reputation: 46455

I believe that this is what you INTEND the combination to be. Though your description is ambiguous, and your existing code does not match it. (As @juvian said, your last 3 conditions as currently written cover the first 4.)

private List<Data> filterData(List<Data> datas, String a, String b, String c) {
    List<Data> result = new ArrayList<>();

    for (Data data : datas) {
        if (! data.getA().equals(a == null ? data.getA() : a)) {
            // Filtered out
        }
        else if (! data.getB().equals(b == null ? data.getB() : b)) {
            // Filtered out
        }
        else if (! data.getC().equals(c == null ? data.getC() : c)) {
            // Filtered out
        }
        else {
            result.add(data);
        }
    }
    return result;
}

Upvotes: 0

juvian
juvian

Reputation: 16068

Here is a more generalized approach: add the strings you want to filter to an array, then add the values from data in same order. Then we can compare them in a more generalized way by using the fact that they will have the same indexes.

List<String> filterBy = new List<String>{a, b, c};
for (Data data : datas) {
    Boolean shouldAdd = true;
    List<String> values = new List<String>{data.getA(), data.getB(), data.getC()};
    for (Integer i = 0; i < filterBy.size(); i++) {
        if (filterBy[i] != null && filterBy[i].equals(values[i]) == false) {
            shouldAdd = false;
            break;
        }
    }
    if (shouldAdd) {
        result.add(data);
    }
} 

Upvotes: 1

Related Questions