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