简体   繁体   中英

How to retrieve a list objects by multiple combination of filters/predicates in Java 8?

I need your help to find out what would be the best way to solve this in java 8

I have a list of objects X that must be classified.

This is my ruleset:

ID  | Filtre1 | Filtre2 | Filtre3 | ValueToApply
1   |  'abc'  |  null   |  null   |  10
2   |  'abc'  |  'def'  |  null   |  25 
3   |  'abc'  |  null   |  'ghi'  |  20

Case 1: I have one object X that contain (value1=abc, value2=def, value3=ghi) so the code should return the list with the rules 1, 2, 3

Case 2: I have one object X that contain (value1=abc, value2=def, value3=hij) so the code should return the list with the rules 1, 2

Thanks

While "best way" is very vague. Here is one interpretation that at least is a good starting point combining a list of rules for filtering fields in objects that's extendable, not limited to the number of fields or objects, easily interchangeable types etc.

First let us define some data objects to capture the data model:

static class Rule {
    final int id;
    final int valueToApply;
    final Predicate<String>[] valueFilters;

    public Rule(int id, int valueToApply, Predicate<String>... valueFilters) {
        this.id = id;
        this.valueToApply = valueToApply;
        this.valueFilters = valueFilters;
    }

    @Override
    public String toString() {
        return "Rule{id="+id+", valueToApply="+valueToApply+"}";
    }
}

static class X {
    final String[] values;

    X(String... values) {
        this.values = values;
    }
}

Then a main method using them with the suggested in data:

public static void main(String[] args) {
    X x1 = new X("abc", "def", "ghi");
    X x2 = new X("abc", "def", "hij");

    List<Rule> rules = new ArrayList<>(Arrays.asList(
            new Rule(1, 10, v->"abc".equals(v)),
            new Rule(2, 25, v->"abc".equals(v), v->"def".equals(v)),
            new Rule(3, 20, v->"abc".equals(v), v->true, v->"ghi".equals(v))
    ));

    Collection<Rule> rulesResult = classify(x1, rules);
    System.out.println("Case 1:"+rulesResult);
    rulesResult = classify(x2, rules);
    System.out.println("Case 2:"+rulesResult);
}

Expected result:

Case 1:[Rule{id=1, valueToApply=10}, Rule{id=2, valueToApply=25}, Rule{id=3, valueToApply=20}]
Case 2:[Rule{id=1, valueToApply=10}, Rule{id=2, valueToApply=25}]

And the "magic" of the classify method.

static Collection<Rule> classify(X x, Collection<Rule> rules) {
    List<Rule> result = new ArrayList<>();

    for (Rule rule : rules) {
        for (int i = 0; i < x.values.length; i++) {
            if (rule.valueFilters.length > i && !rule.valueFilters[i].test(x.values[i]))
                continue;
            if (i == x.values.length-1)
                result.add(rule);
        }
    }

    return result;
}

Nothing major to remark on. The rule.valueFilters.length > i is there so you don't have to specify trailing filters in the rules that all return true.

I'm sure streams could be used but the readability would be questionable and most likely not efficient for a batch job like this were there is no early termination/lazy eval benefits.

Loop over objects of class X.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM