ArrayLogic
A system that allows me to define the logic for comparing arrays of objects.
One prerequisite for the comparison is that the objects have an id method that returns a unique (within the set of objects) integer.
The logic for an active record model Answer, looks like this:
a1 = Answer.find(1)
a2 = Answer.find(2)
....
a5 = Answer.find(5)
rule_one = ArrayLogic::Rule.new "(a1 and a2) or (a3 and a4)"
rule_two = ArrayLogic::Rule.new "a1 and not a2"
rule_three = ArrayLogic::Rule.new "2 in a1 a2 a3"
rule_four = ArrayLogic::Rule.new "(2 in a1 a2 a3) and (1 in a4 a5)"
rule_one rule_two rule_three rule_four
[a1, a2] true false true false
[a3, a4] true false false false
[a1, a3, a5] false true true true
The match and matches methods allow arrays to be tested against these rules:
rule_two.match([a1, a2]) --> false
rule_two.matches([a1, a2], [a1]) --> [[a1]]
You can also test for arrays that do not match the rule by using block and blockers:
rule_two.block([a1, a2]) --> true
rule_two.blockers([a1, a2], [a1]) --> [[a1, a2]]
See test/array_logic/rule_test for more examples
Combinations that match
Two methods allow you to determine sample combinations that match the current rule.
rule = ArrayLogic::Rule.new 'a1 and a2'
rule.matching_combinations --> [[1,2]]
rule.blocking_combinations --> [[1],[2]]
To limit the number of samples presented, both only use ids used within the rule. For the example above, an array that includes [1,2] would match, and so would [1,2,3]. However, arrays that only contain 1 or 2 would not match (for example [1,3])
Run example.rb to see some more examples
ruby /lib/example.rb
Functions
Version 0.2 introduces the concept of functions to ArrayLogic. The function syntax is:
<function>(<object_method as symbol>) <operator> <number>
where:
- function
-
One of the functions listed below
- object_method
-
A method that can be called on all of the objects in the array
- operator
-
one of: <, <=, >, >=, ==, !=
- number
-
a number to compare with the result
Using this array as an example:
answers = [Answer.find(1), Answer.find(5), Answer.find(6)]
sum
Sums the values returned by the object_method and compares them with the number
rule = ArrayLogic::Rule.new 'sum(:id) == 12'
rule.match(answers) --> true
rule = ArrayLogic::Rule.new 'sum(:id) > 12'
rule.match(answers) --> false
rule = ArrayLogic::Rule.new 'sum(:id) >= 12'
rule.match(answers) --> true
average
Averages the values returned by the object_method and compares them with the number
rule = ArrayLogic::Rule.new 'average(:id) == 4'
rule.match(answers) --> true
rule = ArrayLogic::Rule.new 'average(:id) < 4'
rule.match(answers) --> false
rule = ArrayLogic::Rule.new 'average(:id) <= 4'
rule.match(answers) --> true
count
Counts the number of items not returning nil.
rule = ArrayLogic::Rule.new 'count(:id) == 3'
rule.match(answers) --> true
If answer has a method :is_odd? that returned nil if the :id is even:
rule = ArrayLogic::Rule.new 'count(:is_odd?) == 2'
rule.match(answers) --> true
Combining functions with other rules
functions can be combined with other rules:
rule = ArrayLogic::Rule.new 'sum(:id) == 12 and a6'
rule.match(answers) --> true
rule = ArrayLogic::Rule.new '(sum(:id) == 12) and not a6'
rule.match(answers) --> false