Drools: how to iterate list and add to another list - java

I am trying to iterate a ArrayList and add to another ArrayList using jboss drools inside the rule.
I have a pojo with the list.
Class DroolsPojo{
List<String> answers;
//getters and setters
}
My pojo returning a list like {"a","b","c","","",""}. I want to iterate the list and want to add elements which are not equal to ""(not empty elements of the list).
How can I do this with drools?
Is there any way to get the element count which is not equal to "" with the drools.
My rule is like as follows.
rule "rule1"
when
dpojo:DroolsPojo(answers!=null)
then
List list = dpojo.getAnswers();
//want to iterate the list here
end
How to do this with drools?

So the rule just has to fire when the answers instance variable is not null?
Using dialect mvel, something like this should work:
package drools.xxx
dialect "mvel"
import drools.xxx.DroolsPojo
rule "rule1"
when
$dpojo : DroolsPojo(answers!=null)
$answersWithoutEmptyStrings : List() from collect ( String(length > 0) from $dpojo.answers )
then
insert($answersWithoutEmptyStrings)
end
Here I do the collect (iterating) in the when clause.

On the right hand side you just write plain old Java code:
List list = dpojo.getAnswers();
for( Object obj: list ){
String s = (String)obj;
if( s.length() > 0 ){ ... }
}
The parser doesn't like generics (yet?) so you'll have to work around that.

Related

How to simulate 'except' for string lists in for loop in Java?

I have a list of strings (partList) which is a fraction part of another list of strings (completeList) and i want to process an object (through processObj()) within a for loop in a way that the current element of the partList will be retracted from the completeList: when the current iteration is on an element of partList then the processing of the object will involve that element and the rest of elements from completeList i do it like so for now:
for (String el: partList) {
completeList.remove(el);
//process the target object using as parameters el and the rest of the complete list except el...
processObj(el,completeList);
completeList.add(el);
}
Is it the right way of doing it?
Thanks for the enlightenment.
I'm not sure the purpose of removing then adding back to the same list, but you can use a Predicate to accept and process certain values.
Predicate<String> accept = (s) -> {
return true; // accepts all strings; you could use partList.contains(s) here, or !s.equals(el)
}
completeList.stream()
.filter(accept.negate()) // Inverse the predicate to implement "except"
.forEach(processObj);
Replace forEach with map if you want to modify the stream values, then you can collect() to get data back to a list.

Drools: Accessing ArrayList as value in Map

I am trying to access the elements of the ArrayList that is the value of a map.
For example:
{"height": [-10,20]} and I am trying to get the individual value say "-10" in order to make a comparison in the when condition.
Right now i am doing:
rule "test"
when
Params(tol: tolerance) //recieving the Map
eval( tol.get("height").get(0) < 0 )
then
...
end
It says that the get function is not part of the type Object. How do i get to the arraylist value?
Assuming that your classes look something like this:
class Params {
private Map<String, List<Integer>> tolerance;
public Map<String, List<Integer>> getTolerance() { return this.tolerance; }
}
Then you should be able to structure a rule like this:
rule "test"
when
// get the Tolerance map and assign to $tolerance
Params( $tolerance: tolerance )
// get the 'height' list from the $tolerance map, assign to $height
Map( $height: this["height"] ) from $tolerance
// Check if the first integer in the $height list is negative
Integer( this < 0 ) from $height.get(0)
then
...
end
The this[ key ] syntax only works for Maps. Depending on how you've configured Drools and how old the version of Drools you're using is, $height may be extracted as an object, which means you'll have to first convert to list before you use the get(#) method.
Avoid eval whenever possible because the Drools compiler cannot optimize those calls.

Create list of an element of an Object from another list of Objects

The model:
public class MyModel{
private int id;
private String name;
....
....
//getters and setters
}
I have a list of MyModel object:
//First Object to be added to list
MyModel myModelObject1 = new MyModel();
myModelObject1.setId(1);
myModelObject1.setName("abc");
//Second Object to be added to list
MyModel myModelObject2 = new MyModel();
myModelObject1.setId(2);
myModelObject1.setName("pqr");
List<MyModel> myModelList = new ArrayList<MyModel>();
myModelList.add(myModelObject1);
myModelList.add(myModelObject2);
I want to get a list of names present in the MyModel List i.e. I want to create a list of names (List<String> in this case) from myModelList. So, I want my list to have:
{"abc", "pqr"}
There is always a way to iterate and create another list but is there any better way to do that? (Not necessarily to be efficient but if it can be done in a line using streams, foreach e.t.c.).
EDIT:
The answers worked for me but I have some modifications in my use case: If I want to add a condition that only name which contains character 'a' should be added to the list and I want to add a logger message to debug for each element then how should I approach this?
I tried doing the following using a method (charAPresent()) which checks that if the passed String contains character 'a' but it didn't work:
List<String> modelNameList = myModelList.stream()
.map(model -> {
if (charAPresent(model.getName)) {
model.getName()
}
})
.collect(Collectors.toList());
Let me know if I am missing something.
Using Java 8 Stream:
List<String> modelNameList = myModelList.stream()
.map(Model::getName)
.collect(Collectors.toList());
Model::getName is called as method reference. It equivalents to model -> model.getName()
You can use streams and map your object to its name and collect to a list as:
List<String> names = myModelList.stream()
.map(MyModel::getName)
.collect(Collectors.toList());
There is always a way to iterate and create another list but is there
any better way to do that
Even with the use of streams, you would have to iterate the complete collection. There is no better way of doing it than iterating the complete collection.
You can use Stream.map() to get only the names of your model. Use Stream.filter() to get only the names matching your charAPresent() method. To log the entries before collecting you can use Stream.peek():
List<String> modelNameList = myModelList.stream()
.map(Model::getName) // map only the name
.filter(YourClass::charAPresent) // filter the items
.peek(System.out::println) // print all items which are in the filter
.collect(Collectors.toList());
You can also use foreach like this:
public static List<String> modelNames(List<MyModel> myModelList)
List<String> list = new ArrayList<String>();
for(MyModel mm : myModelList) {
if(mm.getName().contains("a") {
list.add(mm.getName());
}
}
return list;
}

Display duplicate attribute values once in Java object list

I have a list of java objects as below
I want to convert it to a list with 'Camera flash faulty' and 'Camera Housing Damaged' only once for the first object, like the one below.
Is there something that can be done with the solution mentioned here ?
Remove duplicates from a list of objects based on property in Java 8
You can add all the properties to a Set (Sets don't allow duplicates), if the value is already in the set, the method add returns false, so you can set to empty the property in the object:
Set<String> values = new HashSet<>();
for (MyObject obj : myList) {
if (!values.add(obj.getValue())) {
obj.setValue("");
}
}
Another alternative is to group all object with the same attribute value, then skip the first element of each group and set the attribute value to empty for all other objects in the group:
myList.stream()
.collect(Collectors.groupingBy(MyObject::getValue))
.forEach((k, v) -> v.stream().skip(1).forEach(o->o.setValue("")));
I am taking an example of Employee which have some duplicate id's.
Lets say you have a list as
List<Employee> employee
Add them into HashSet, which does not allow duplicates.
HashSet<Object> seen=new HashSet<>();
employee.removeIf(e->!seen.add(e.getID()));

how to add a list to another list while looping?

I have a list which has object(record) taken from database. I need to add it another list of generic class inside a loop.When ever loop executes the final list contains only the last element.my coding are..
List<modelclass> mdlclasslist=new ArrayList();
for(Class_1 a:class1list) {
Query qr=s.createQuery("from Class_2 where ID= :f and code= :j order by mark desc");
qr.setParameter("f",id);
qr.setParameter("j",code);
List<Class_2> b=new ArrayList();
b=qr.list();
for(Class_2 cls:b) {
modelclass mdl=new modelclass(cls.getID(),cls.getCode(),cls.getMark());
mdlclasslist.add(mdl);
}
}
mdlclasslist contains same object.It is not adding every object the query takes.please advice.
Your Query appears to return the same list over and over again for every Class_1 item because id and code never change. I assuming your code should rather look like this:
Query qr=s.createQuery("from Class_2 where ID= :f and code= :j order by mark desc");
for( Class_1 a : class1list )
{
qr.setParameter( "f", a.id );
qr.setParameter( "j", a.code );
for( Class_2 cls: qr.list() )
{
modelclass mdl=new modelclass(cls.getID(),cls.getCode(),cls.getMark());
mdlclasslist.add(mdl);
}
}
How about debugging and printing out the number of elements in the 2nd list before adding?
Not sure if you want to append the List you retrieve from a DB to the one you initialize beforehand...
However, I would define the 1st List to be of the generic type Class_1 (BTW: read about Java naming conventions) and then use addAll
yourList.addAll(theListFromDB);
try this
listInstance.addAll(anotherListInstavce) ;
First i would check if my source list, the one populated from DB has more than 1 element. If you are using JDBC, its a very common mistake to not move the result set objects further.
Secondly if you need such collection manipulation utilities i suggest take a look at commons-collections ListUtils class.
All the collections have a simple method to add data of one collection to other.
list2.addAll(list1);
You can simply use this method...

Categories