Samyak
Samyak

Reputation: 81

Drools check if one list contains all elements of another list

How to check whether list of facts contains the list of parameters or attributes present. We want a Rule where The Fact is List of Variables and the attributes which we are passing it is also list of variables. We want to check whether the the Facts contains all the variables present in the List of Attributes . And if it has all the variables matching the Fact then should execute the Action part

    when
          variable:Fact(names contains( ${names}))
    then
          System.out.println($listOfNames);
    end

This is the example which I have tried. Here the Fact class has the Variable name which is List of String. And ${names} is the attribute which we are passing it as the List of String. So basically we want is that when we pass the list of names in fact it shold see whether it has the variable present from ${names} if yes then should execute. But when we pass list to attribute it gives error which is

11:21:00.868 [main] ERROR o.d.c.k.b.impl.AbstractKieProject.buildKnowledgePackages:276 - Unable to build KieBaseModel:defaultKieBase
Unable to Analyse Expression names contains ( [TaskSpecification3, TaskSpecification4]):
[Error: unable to resolve method using strict-mode: com.drool.example.Fact.TaskSpecification3()]
[Near : {... names contains ( [TaskSpecification3, TaskSpecif ....}]
                               ^ : [Rule name='Rule1_1']

It is not able to rectify bracket, when we pass attributes in the list form it gets print with brackets which gives error.

Is there any way we can Compare that One list contains the parameters from the other List.

Upvotes: 0

Views: 1120

Answers (2)

Samyak
Samyak

Reputation: 81

template header
salience
names
namedTags

import com.drool.example.Fact;
import java.util.Map;
import java.util.List;
global java.util.List list;

function Boolean toCompareList(List targetList, List blackList){

    Boolean flag = false;
    for(Object obj: targetList){
        if(blackList.contains(obj)){
            flag = true;
            break;
        }
    }
    return flag;
}

template "DataWithoutNull"

rule "Rule1_@{row.rowNumber}"
 salience @{salience}
 dialect "java"
when
  $names: Fact($listOfNames:${names})
  variable:Fact(toCompareList(names,$listOfNames))
then
  System.out.println($names);
end

This is the Drt which I have used. Here we have written a function to check if onle list contains all the element in other function and that function is used in the Condition part where we pass facts and parameters to that function and then it evaluate it and give the result in true or false.

If its true then the action part will be executed or will look for other condition and if false will not execute the action part.

Is this the right way to do it or is there any other method. But this works absolutely fine as we expected. If there is any other method please let me know.

Upvotes: 0

Roddy of the Frozen Peas
Roddy of the Frozen Peas

Reputation: 15190

I don't even know where to start, that rule's syntax is so wrong.


For your actual question -- how to check if one list contains all elements of another list -- this is a straight-forward ask and there are a number of possible ways to go about it.

From your question, you have a model that looks like this:

class Fact {
  List<String> names;
  // getters, setters
}

You're passing a Fact instance into your rules, along with a List<String> representing target names. You want your rule to trigger when all of the names in the list in memory are present in the list of names in the fact.

Based on this, your test cases would be something like:

TC | Fact                           | List             | Result
---|--------------------------------|------------------|-------------------
1  | Fact( names: ["A", "B", "C"] ) | List: ["A", "B"] | rule fires / match
2  | Fact( names: ["A", "B", "C"] ) | List: ["A", "D"] | no match
3  | Fact( names: ["A"] )           | List: ["A", "B"] | no match
4  | Fact( names: ["A", "B"] )      | List: ["A", "B"] | rule fires / match

As I mentioned, we can write this rule in several ways. The easiest way to go about this would be to use collect.

rule "Fact check with collect"
when
  $names: List( $expected: size )
  Fact( $factNames: names )

  $matches: List( size == $expected ) from collect( 
    String( this memberOf $names ) from $factNames
  )
then
  System.out.println($matches);
end

What we're doing here is straight-forward. First, we get the list of names and assign it to the variable $names. We also save the length of that list to $expected. Then we extract the list of names from Fact and assign it to the variable $factNames.

Next we use collect to iterate over each name in $factNames, check that it is in the $names list, and (if it is), put it into a List called $matches. We check that $matches is the same length as the $names list -- if it is, then the rule triggers and we move to the consequences (right hand side/then.)

  • More info on collect: see the Drools documentation (search for "Figure 82." to jump as close as you can to the unanchored section about this operator.

If the list of names is not passed as a fact, but is part of the rule, then it simplifies the case further and you can just use in to verify.

Assuming a hard-coded list of ["A", "B", "C"]:

rule "Fact check with collect and hard-coded list"
when
  Fact( $factNames: names )

  $matches: List( size == 3 ) from collect( 
    String( this in ("A", "B", "C") ) from $factNames
  )
then
  System.out.println($matches);
end

Note that in addition to the actual content of the list, you also have to sub in the length of the list in the size == check.

Upvotes: 1

Related Questions