eliezerb
eliezerb

Reputation: 47

Get similar objects in Drools

Assuming I have a list of objects from the type shirt, and each shirt has a color property. Is there a way to create a rule which will get only shirts of the same color (no matter what the color is)?

Upvotes: 1

Views: 42

Answers (1)

Roddy of the Frozen Peas
Roddy of the Frozen Peas

Reputation: 15219

You're looking for the collect function. (Link is to the Drools documentation, scroll down a bit to find "collect".) Like its name indicates, it collects things that match a condition.

Let's assume a simple class Shirt with a String color variable. Let's also assume that there are a variety of shirt instances in working memory.

rule "Collect shirts by color"
when
  Shirt( $color: color )
  $shirts: List() from collect( Shirt( color === $color ) )
then
  // $shirts will now contain all shirts of $color
end

This rule will individually consider each shirt, and then collect all of the other shirts which have the same color. So if you have Red, Blue, and Green shirts, you'll enter the right hand side at least once with $shirts of that single color.

Of course the problem here is that you'll trigger the rule on a per-shirt basis instead of on a per-color basis. So if you have two Red shirts, you'll trigger the 'then' clause with all red shirt twice (once for each red shirt, since each red shirt will independently meet the first criteria.)

If you don't mind this, then you can use this as-is. But if you just want your consequences to fire once per shirt color, we have to be a bit more tricksy.

In order for colors to be the first class citizen, we'll need to extract the distinct set (not list!) of shirt colors, and then use those to collect our lists as needed. Here I use the accumulate function; you can read more about that at the same link I shared previously to the Drools documentation (accumulate is directly after collect.)

rule "Get shirt colors"
when
  // Get all unique shirt colors
  $colors: Set() from accumulate( Shirt( $color: color), collectSet( $color ))

  // Get one of those colors
  $color: String() from $colors

  // Find all shirts of that color
  $shirts: List() from collect( Shirt( color === $color ) )
then
  // $shirts is all shirts of $color
end

In this second rule, you will only trigger the right hand side once per color because we started by distilling all possible colors into a distinct set of unique ones.


Doing the opposite is even simpler. If all you need to do is confirm there is at least one shirt that is not the same color, we just need to get all of the colors and verify that there's at least 2 different colors present.

rule "At least one shirt of a different color"
when
  $colors: Set( size > 1 ) from accumulate( 
    Shirt( $color: color), 
    collectSet( $color )
  )
then
  // $colors contains more than 1 color, so there's at
  // least 1 shirt present that is not the same color
  // as the rest
end

Upvotes: 1

Related Questions