0

Let's say I have some numbers in the working memory, and in a rule I need to check that the 3 higher numbers have a property (that they are all odd for example), how would you write that in pure Drools?

I could collect all the numbers in a list and then have a Java function that filters it taking just the best 3 (like order the list and take the last 3) and then check the property on those 3 with Drools, but I wonder if this can be done in pure Drools maybe with an accumulate?

I have tried to think for a bit but couldn't find a solution.

Francesco
  • 857
  • 1
  • 11
  • 26
  • I'm alway willing to help, but do you really need to do arithmetic exercises?? – laune Feb 04 '15 at 14:59
  • I was trying to keep the example easy, at the end in my business case I have no numbers but qualifications with grades, but at the end is the same thing. – Francesco Feb 04 '15 at 18:41
  • This kind of thing is usually clearer using objects with a numeric field. And, with suitable background information, one might suggest things like doing a (Java) sort. Sorting is one thing rules (not just Drools) don't do too well. – laune Feb 04 '15 at 18:45

2 Answers2

2
rule best3
when
  $o1: Integer( $i1: intValue, intValue % 2 == 0 )
  not Integer( intValue > $i1 )
  $o2: Integer( this != $o1, $i2: intValue, intValue % 2 == 1 )
  not Integer( intValue > $i2 && < $i1 )
  Integer( this != $o1 && != $o2, $i3: intValue, intValue % 2 == 1 )
  not Integer( intValue > $i3 && < $i2 )
then
  System.out.println( $i1 + " > " + $i2 + " > " + $i3 );
end
laune
  • 31,114
  • 3
  • 29
  • 42
  • Thank you laune, I ended up doing this but I was wondering if there is a more concise way of doing it? or you cannot avoid to to all the possible checks between the numbers? it's fine for "the best 3", but for "the best 9" it will become very long – Francesco Feb 04 '15 at 18:43
  • 1
    That's just the point... You ask for it, you get it, you see it, you don't like it... – laune Feb 04 '15 at 18:46
  • What do you mean? I have not said I don't like it. I am just asking if there is another way of doing it. If there is not, I think a no as an answer is enough. Thank you anyway for your help. – Francesco Feb 04 '15 at 20:34
  • I meant to say that asking for the "real thing" gets better results, faster. The Q was for "the best 3", but actually you want the "best 9" or "best N". If asked for that, I'd have said Arrays.sort or some such thing right away, it would have saved me some time and both of us this rigmarole. – laune Feb 04 '15 at 20:44
  • Unfortunately I cannot predict the future (yet) and I came out with your solution after I made the first question, but found it too long. I was hoping of a better one and so I asked, still not sure what's wrong with this. Anyway I marked yours as best answer. – Francesco Feb 04 '15 at 20:50
0

As my hint in the question, this can be achieved more elegantly with a custom accumulate function:

rule "odd"
    when
        accumulate($l : Integer(), $r : reverseSort($l); $r.size > 2, ($r[0] % 2) == 1, ($r[1] % 2) == 1, ($r[2] % 2) == 1)
    then
        System.out.println("Best 3 are odd numbers: " + $r);
end

and the accumulate function:

package com.test;

import java.io.*;
import java.util.*;

import org.kie.api.runtime.rule.AccumulateFunction;

public class ReverseSortAccumulator implements AccumulateFunction {
    private static class accumulatorData implements Serializable {   
        private static final long serialVersionUID = 1L;

        private List<Object> elements = new ArrayList<>();

        private List<Object> getElements() {
            return elements;
        }
        public void init() {
            getElements().clear();
        }

        public void add(Object element) {
            getElements().add(element);
        }

        public void remove(Object element) {
            getElements().remove(element);
        }

        public List<Object> reverseSort() {
            return getElements().stream()
                    .sorted(Collections.reverseOrder())
                    .collect(Collectors.toList());
        }
    }

    @Override
    public Serializable createContext() {
        return new accumulatorData();
    }

    @Override
    public void init(Serializable context) {
        getContextData(context).init();
    }

    @Override
    public void accumulate(Serializable context, Object input) {
        getContextData(context).add(input);
    }

    @Override
    public void reverse(Serializable context, Object input) {
        getContextData(context).remove(input);
    }

    @Override
    public List<?> getResult(Serializable context) {
        return getContextData(context).reverseSort();
    }

    @Override
    public boolean supportsReverse() {
        return true;
    }

    @Override
    public Class<?> getResultType() {
        return List.class;
    }

    private accumulatorData getContextData(Serializable context) {
        return (accumulatorData) context;
    }

    @Override
    public void writeExternal(ObjectOutput out) {
        // no need to externalise data over sessions
    }

    @Override
    public void readExternal(ObjectInput in) {
        // no need for externalised data from sessions
    }

}
Francesco
  • 857
  • 1
  • 11
  • 26