-1

I am new to Drools and i am struggling to find the solution for below problem:

I have a list of Account class:

class Account {
    private String name;
    private float amount;
}

I want to group the list of Account using name in drools. For example:

Account a = new Account("Science", 100);
Account b = new Account("Science", 200);
List<Account> list = new ArrayList<String>();
list.add(a);
list.add(b);

Now I need a drool rule that should group the elements inside the list using name and provide the list that will have "Science, 300".

Please suggest.

john
  • 1
  • 1
    In extreme situations a spanner can be used as a hammer; however, a spanner is not designed to be used as a hammer. Drools is not a good fit for your use case. Suggestion is to use util streams or a more direct approach. – Ironluca Nov 06 '20 at 09:26

2 Answers2

0

As Ironluca points out in the comments, you are using the wrong tool for the job. The correct solution would be to use Java streams; even a simple for-loop would solve your problem.

Of course, since you can use a hammer on a screw, you can do this with Drools. I will include an example rule and an explanation, though it will be much less efficient than a stream-based solution (or even a for-loop.)

I will assume this model, with appropriate getters, setters, and an all-args constructor:

class Account {
  String name;
  int quantity;
}

Then this rule will populate an 'output' list with the "grouped" accounts. I've declared this 'output' list as a global, which isn't best-practice but none of this is recommended anyway since it's not the appropriate tool for the job.

global List $output;

rule "Consolidate duplicates"
when
  $inputs: List()
  Account( $name: name ) from $inputs
  $duplicates: List() 
               from collect( Account( name == $name ) from $inputs )
  $total: Number()
          from accumulate( Account( $value: quantity ) from $duplicates, sum( $value ) )
then
  $output.add(new Account($name, $total));
end

The first part of the rule identifies the subset of Account instances in the input list which share the same name. I use collect because this is a simple subset of the original input. The next step is to use accumulate to sum up the quantities from those duplicates. Then we add a new instances to the output list on the rule's RHS with the consolidated quantity and the shared name. Note that the rule works the same for Accounts which have unique names; there is no constraint on length for $duplicates, and the accumulation would just be the one identified quantity.

Modifying $inputs in-place is also do-able, but tricky, since you have to worry about concurrent modification exceptions. The much cleaner implementation is to use


Of course this is way more complicated than it has to be. Java 8 streams provide a groupingBy collector which allows you to trivially create a map by a property within the object:

Map<String, List<Account>> accountsByName = accounts.stream()
  .collect(groupingBy(Account::getName));

You could then easily transform this by iterating over the valueset.

This other question goes into detail for other 'summation of duplicates in a stream' scenarios: Java 8 stream sum entries for duplicate keys

Roddy of the Frozen Peas
  • 14,380
  • 9
  • 49
  • 99
0

Using groupby, introduced in Drools 8.41.0.Final, this is now possible:

rule "Sum account amount by name"
when
    groupby(
        $account: /accounts;
        $name: $account.name;
        $total: sum($account.amount)
    )
then
    System.out.println("Account " + $name + " has a total of " + $total);
end

(the example given use the OOPath syntax, but the traditional syntax is also supported)

Christopher Chianelli
  • 1,163
  • 1
  • 8
  • 8