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
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
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])
Combinations and functions¶ ↑
Combinations are determined by analysing arrays of integers matching the ids found in the rule. This works for most rules but not those that contain functions.
Therefore, when either matching_combinations, or blocking_combinations are called on a rule that contains a function, an exception is raised.
rule = ArrayLogic::Rule.new 'sum(:id) > 5' rule.matching_combinations --> raises ArrayLogic::UnableToDetermineCombinationsError
Thereby handling this issue is passed back to the host application
Run example.rb to see some more examples
ruby /lib/example.rb