Francesco
Francesco

Reputation: 907

Finding the best 3 elements in a collection in Drools

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.

Upvotes: 0

Views: 877

Answers (2)

Francesco
Francesco

Reputation: 907

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
    }

}

Upvotes: 0

laune
laune

Reputation: 31300

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

Upvotes: 2

Related Questions