When modeling business rules, there are some scenarios when you need to deal with a collection/set of data, such as operations on the sequencing of the data (i.e., aggregating, grouping, joining etc). In this post, we are going to model a decision to identify duplicate elements in a list. This is a very interesting problem to solve. That’s because in business rules and decision modeling, we want to solve this problem in a declarative way.
Let’s consider the following set of data (list of persons):
In BRMS solutions there are couple of options to accomplish this:
- Define a custom function (e.g., user defined functions) to process the data
- Define two iterators (loops) and compare items one-by-one based on certain criteria
Nothing is wrong with either of those solutions. However, when you rely on a user defined function, then firstly you lose the control of the approach to solve the problem, and secondly you are back to software development (and programming), which you had hoped to avoid by using BRMS in the first place. Also, if you define two loops and compare items on the list, then you are implementing the actual algorithm, so you lose the beauty of defining a declarative model to answer a question. Finally, this approach might not be as reusable as you would ideally need across different, but similar problems.
What’s the Answer?
Let’s see how we do it in the old school way, as if it was supposed to be done later using data (i.e., database TSQL queries).
SELECT Name, Sex, Count(*) From Persons Group By Name, Sex Having Count(*) > 1
So let’s see what do we need to do it similarly using rule expressions
- Select: projector which allows us to select properties (or more complex expressions)
- From: source of list
- Group By: a way of grouping based on single or composite values
- Having: condition and criteria
Now let’s take a similar approach in rule expressions (monadic operators):
Persons |groupBy (x, x.Name, x.Sex) |where (g, g.Count > 1) |select (s, s.Key.Name)
As you can see, there are similarities between monadic operators and TSQL query. You do group by Name and Sex and filter where Count is greater than 1 and then select the Names from the list.
Now we have a declarative, reusable solution that does not require user defined custom functions and which is coded in your programming language. So let’s put it in a Decision Table and see how that works. The resulting Decision Table is fairly simple now, as all the identifying duplicate items are expressed in a declarative rule expression.
The first row has no condition, and it executes the above expression as we discussed, then stores the result into memory. The second row checks afterwards to see if there are any duplicates from the previous rule (previous row) then it writes error messages for each of these.
As you may have just noticed, ‘Action’ in the Decision Table can be applied to collections (array of elements). To do that, you just need to set the Source and Element Name of the Array section in the Action column.
And that’s it! When you run the Decision Table now, you get the result shown below:
As you can see, because we created a grouping key based on Name and Sex (key(x.Name, x.Sex)) it only lists ‘Andy’, whose combination of his Name and Sex is not unique in the provided list (persons).
Of course, you could do something like this:
Persons |groupBy (x, x.Name) |where (g, g.Count > 1) |select (s, s.Key)
And then the result would have been like this:
When you group only on the Name of the person you see the above result.
GroupBy, Where, and Select are Monadic Operators that can be chained together by a pipe to accomplish a complex task. To learn more on other Monadic operations please visit http://wiki.flexrule.com/index.php?title=Expression/Pipes
I will post more details on business rule expressions and monads in future articles.