operational-decision-tree
Advanced tools
Comparing version 1.1.1 to 2.0.0
@@ -9,8 +9,3 @@ | ||
var conditions = require('./conditions.js') | ||
var opts = { | ||
conditions: conditions | ||
} | ||
var person = { | ||
@@ -20,6 +15,8 @@ name: "Bob", | ||
nationality: 'US', | ||
random: Math.floor(Math.random()*100) | ||
random: function (condition, subjectData, cb) { | ||
cb(null, Math.floor(Math.random()*100)) | ||
} | ||
} | ||
var DecisionTree = new DTS(opts) | ||
var DecisionTree = new DTS() | ||
DecisionTree.run(treeData, person, function (err, result) { | ||
@@ -26,0 +23,0 @@ if (err) console.error("ERROR", err) |
@@ -5,4 +5,5 @@ | ||
"name": "conditionAge", | ||
"opts": { | ||
"operator": ">", | ||
"property": "age", | ||
"comparison": { | ||
"operation": ">", | ||
"value": 40 | ||
@@ -15,10 +16,21 @@ } | ||
"name": "conditionPercent", | ||
"opts": { | ||
"split": [ | ||
30, | ||
20, | ||
40, | ||
10 | ||
] | ||
} | ||
"property": "random", | ||
"comparisons": [ | ||
{ | ||
"operation": "<", | ||
"value": 30 | ||
}, | ||
{ | ||
"operation": "<", | ||
"value": 50 | ||
}, | ||
{ | ||
"operation": "<", | ||
"value": 90 | ||
}, | ||
{ | ||
"operation": "<", | ||
"value": 100 | ||
} | ||
] | ||
}, | ||
@@ -35,4 +47,6 @@ "branches": [ | ||
"name": "conditionCountry", | ||
"opts": { | ||
"value": "US" | ||
"property": "nationality", | ||
"comparison": { | ||
"operation": "in", | ||
"value": ["US", "CA"] | ||
} | ||
@@ -44,4 +58,5 @@ }, | ||
"name": "conditionAge", | ||
"opts": { | ||
"operator": "<", | ||
"property": "age", | ||
"comparison": { | ||
"operation": "<", | ||
"value": 60 | ||
@@ -48,0 +63,0 @@ } |
@@ -7,6 +7,5 @@ | ||
var treeData = require('./binary-tree.json') | ||
var conditions = require('./conditions.js') | ||
var opts = { | ||
conditions: conditions, | ||
// custom decider | ||
decider: function (result) { | ||
@@ -13,0 +12,0 @@ if (result) { |
@@ -5,4 +5,5 @@ | ||
"name": "conditionAge", | ||
"opts": { | ||
"operator": ">", | ||
"property": "age", | ||
"comparison": { | ||
"operation": ">", | ||
"value": 40 | ||
@@ -15,4 +16,5 @@ } | ||
"name": "conditionAge", | ||
"opts": { | ||
"operator": ">", | ||
"property": "age", | ||
"comparison": { | ||
"operation": ">", | ||
"value": 20 | ||
@@ -29,9 +31,11 @@ } | ||
"name": "conditionCountry", | ||
"opts": { | ||
"value": "US" | ||
"property": "nationality", | ||
"comparison": { | ||
"operation": "in", | ||
"value": ["US", "CA"] | ||
} | ||
}, | ||
"branches": [ | ||
{"result": "Leaf C: Over 40 and not from US"}, | ||
{"result": "Leaf D: Over 40 and from the US"} | ||
{"result": "Leaf C: Over 40 and not from North America"}, | ||
{"result": "Leaf D: Over 40 and from the North America"} | ||
] | ||
@@ -38,0 +42,0 @@ } |
@@ -7,14 +7,3 @@ | ||
var treeData = require('./multi-condition-nodes.json') | ||
var conditions = require('./conditions.js') | ||
var opts = { | ||
conditions: conditions, | ||
decider: function (result) { | ||
if (result) { | ||
return 1 | ||
} else { | ||
return 0 | ||
} | ||
} | ||
} | ||
@@ -28,3 +17,3 @@ var person = { | ||
var DecisionTree = new DTS(opts) | ||
var DecisionTree = new DTS() | ||
DecisionTree.run(treeData, person, function (err, result) { | ||
@@ -31,0 +20,0 @@ if (err) console.error("ERROR", err) |
@@ -5,3 +5,4 @@ | ||
"name": "conditionAge", | ||
"opts": { | ||
"property": "age", | ||
"comparison": { | ||
"operator": ">", | ||
@@ -16,3 +17,4 @@ "value": 70 | ||
"name": "conditionAge", | ||
"opts": { | ||
"property": "age", | ||
"comparison": { | ||
"operator": ">", | ||
@@ -24,3 +26,4 @@ "value": 18 | ||
"name": "conditionAge", | ||
"opts": { | ||
"property": "age", | ||
"comparison": { | ||
"operator": "<", | ||
@@ -39,3 +42,4 @@ "value": 60 | ||
"name": "conditionCountry", | ||
"opts": { | ||
"property": "nationality", | ||
"comparison": { | ||
"value": "US" | ||
@@ -42,0 +46,0 @@ } |
114
lib/index.js
//var async = require('async') | ||
DecisionTree = module.exports = function (opts) { | ||
if (!opts.conditions) { | ||
return new Error("Must specify all condition functions!") | ||
if (opts) { | ||
this.opts = opts | ||
} else { | ||
this.opts = {} | ||
} | ||
this.opts = opts | ||
} | ||
@@ -48,16 +48,100 @@ | ||
DecisionTree.prototype.testCondition = function (condition, subjectData, cb) { | ||
if (this.opts.conditions[condition.name]) { | ||
var fn = this.opts.conditions[condition.name] | ||
if (this.opts.conditions[condition.name].run) { | ||
fn = this.opts.conditions[condition.name].run | ||
} | ||
fn(condition.opts, subjectData, function (err, result) { | ||
if (err) return cb(err) | ||
cb(null, result) | ||
var self = this | ||
var priority = {} | ||
if (isFunction(subjectData[condition.property])) { | ||
subjectData[condition.property](condition, subjectData, function (err, result) { | ||
priority[condition.property] = result | ||
var result = self.runComparisons(condition, subjectData, priority) | ||
return cb(null, result) | ||
}) | ||
} else { | ||
cb(new Error('Condition named: "'+condition.name+'" does not exist!')) | ||
var result = self.runComparisons(condition, subjectData, priority) | ||
return cb(null, result) | ||
} | ||
} | ||
DecisionTree.prototype.runComparisons = function (condition, subjectData, priority) { | ||
if (condition.comparison) { | ||
var result = this.compare(condition.property, condition.comparison, subjectData, priority) | ||
return result | ||
} else if (condition.comparisons) { | ||
for (var index in condition.comparisons) { | ||
var result = this.compare(condition.property, condition.comparisons[index], subjectData, priority) | ||
if (result) { | ||
return index | ||
} | ||
} | ||
return 0 | ||
} | ||
} | ||
DecisionTree.prototype.compare = function (property, comparison, subjectData, priority) { | ||
var result = 0; | ||
if (priority[property]) { | ||
var val = priority[property] | ||
} else { | ||
var val = subjectData[property] | ||
} | ||
switch (comparison.operation) { | ||
case ">": | ||
if (val > comparison.value) { | ||
result = 1; | ||
} | ||
break; | ||
case ">=": | ||
if (val >= comparison.value) { | ||
result = 1; | ||
} | ||
break; | ||
case "<": | ||
if (val < comparison.value) { | ||
result = 1; | ||
} | ||
break; | ||
case "<=": | ||
if (val <= comparison.value) { | ||
result = 1; | ||
} | ||
break; | ||
case "==": | ||
if (val == comparison.value) { | ||
result = 1; | ||
} | ||
break; | ||
case "===": | ||
if (val === comparison.value) { | ||
result = 1; | ||
} | ||
break; | ||
case "!=": | ||
if (val != comparison.value) { | ||
result = 1; | ||
} | ||
break; | ||
case "!==": | ||
if (val !== comparison.value) { | ||
result = 1; | ||
} | ||
break; | ||
case "in": | ||
for (var i in comparison.value) { | ||
if (val === comparison.value[i]) { | ||
result = 1; | ||
} | ||
} | ||
break; | ||
case "nin": | ||
result = 1; | ||
for (var i in comparison.value) { | ||
if (val === comparison.value[i]) { | ||
result = 0; | ||
} | ||
} | ||
break; | ||
default: | ||
return new Error('Invalid comparison operation') | ||
} | ||
return result | ||
} | ||
DecisionTree.prototype.testConditions = function (conditions, subjectData, cb) { | ||
@@ -145,1 +229,5 @@ var results = [] | ||
function isFunction(functionToCheck) { | ||
var getType = {}; | ||
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; | ||
} |
{ | ||
"name": "operational-decision-tree", | ||
"version": "1.1.1", | ||
"version": "2.0.0", | ||
"description": "A Decision Tree executor that uses decision modules to decide pathways. Can accept binary or arbitrary decision modules.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -15,6 +15,17 @@ # Operational-Decision-Tree # | ||
For each condition, ODT compares the subject data to the tree data based on the "operation" parameter you pass in the tree. Supported operations are: | ||
* > subject value is greater than tree comparison value | ||
* >= subject value is greater than or equal to tree comparison value | ||
* < subject value is less than tree comparison value | ||
* <= subject value is less than or equal to tree comparison value | ||
* == subject value is equal to tree comparison value | ||
* === subject value is definitely equal to tree comparison value | ||
* != subject value is not equal to tree comparison value | ||
* !== subject value is definitely not equal to tree comparison value | ||
* in subject value is present in tree comparison value (tree comparison value must be an array) | ||
* nin subject value is not present in tree comparison value (tree comparison value must be an array) | ||
Several things are required to use an ODT: | ||
* The executor (this package) | ||
* The tree data (provided by you) | ||
* The decision condition functions (provided by you) | ||
* The decision maker (a default decision maker is included in this package, but you can insert your own if need be) | ||
@@ -34,4 +45,5 @@ | ||
"name": "conditionAge", | ||
"opts": { | ||
"operator": ">", | ||
"property": "age", | ||
"comparison": { | ||
"operation": ">", | ||
"value": 40 | ||
@@ -44,4 +56,5 @@ } | ||
"name": "conditionAge", | ||
"opts": { | ||
"operator": ">", | ||
"property": "age", | ||
"comparison": { | ||
"operation": ">", | ||
"value": 20 | ||
@@ -58,9 +71,11 @@ } | ||
"name": "conditionCountry", | ||
"opts": { | ||
"value": "US" | ||
"property": "nationality", | ||
"comparison": { | ||
"operation": "in", | ||
"value": ["US", "CA"] | ||
} | ||
}, | ||
"branches": [ | ||
{"result": "Leaf C: Over 40 and not from US"}, | ||
{"result": "Leaf D: Over 40 and from the US"} | ||
{"result": "Leaf C: Over 40 and not from North America"}, | ||
{"result": "Leaf D: Over 40 and from the North America"} | ||
] | ||
@@ -77,26 +92,2 @@ } | ||
var opts = { | ||
conditions: { | ||
conditionAge: function (opts, data, cb) { | ||
if (eval(data.age+opts.operator+opts.value)){ | ||
return cb(null, true) | ||
} | ||
return cb(null, false) | ||
}, | ||
conditionCountry: function (opts, data, cb) { | ||
if (data.nationality === opts.value) { | ||
return cb(null, true) | ||
} | ||
return cb(null, false) | ||
} | ||
}, | ||
decider: function (result) { | ||
if (result) { | ||
return 1 | ||
} else { | ||
return 0 | ||
} | ||
} | ||
} | ||
var person = { | ||
@@ -109,3 +100,3 @@ name: "Bob", | ||
var DecisionTree = new ODT(opts) | ||
var DecisionTree = new ODT() | ||
DecisionTree.run(treeData, person, function (err, result) { | ||
@@ -112,0 +103,0 @@ if (err) console.error("ERROR", err) |
@@ -12,3 +12,3 @@ | ||
tape('Test Basic Condition', function (t) { | ||
var DT = new DTS({conditions:conditions}) | ||
var DT = new DTS() | ||
@@ -21,3 +21,7 @@ var subject = { | ||
name: 'conditionBasic', | ||
opts: {} | ||
property: 'value', | ||
comparison: { | ||
operation: '>', | ||
value: 0 | ||
} | ||
} | ||
@@ -36,6 +40,6 @@ | ||
tape('Test Condition With Opts', function (t) { | ||
var DT = new DTS({conditions:conditions}) | ||
var DT = new DTS() | ||
var subject = { | ||
value: 1 | ||
somevalue: 1 | ||
} | ||
@@ -45,3 +49,5 @@ | ||
name: 'conditionOpts', | ||
opts: { | ||
property: 'somevalue', | ||
comparison: { | ||
operation: '<', | ||
value: 2 | ||
@@ -53,3 +59,3 @@ } | ||
t.ok(result, 'Condition with opts returns true') | ||
subject.value = 3 | ||
subject.somevalue = 3 | ||
DT.testCondition(condition, subject, function (err, result) { | ||
@@ -63,3 +69,3 @@ t.notOk(result, 'Condition with opts returns false') | ||
tape('Test runNode', function (t) { | ||
var DT = new DTS({conditions:conditions}) | ||
var DT = new DTS() | ||
@@ -73,4 +79,5 @@ var subject = { | ||
name: "conditionAge", | ||
opts: { | ||
operator: ">", | ||
property: 'age', | ||
comparison: { | ||
operation: ">", | ||
value: 20 | ||
@@ -100,3 +107,3 @@ } | ||
tape('Test defaultDecider', function (t) { | ||
var DT = new DTS({conditions:conditions}) | ||
var DT = new DTS() | ||
@@ -128,3 +135,3 @@ var result | ||
tape('Test countConditions', function (t) { | ||
var DT = new DTS({conditions:conditions}) | ||
var DT = new DTS() | ||
DT.countConditions(sampleTree, function(err, conditionCount) { | ||
@@ -137,9 +144,1 @@ t.equal(1, conditionCount.conditionPercent, 'countConditions returned the correct number of "conditionPercent"') | ||
tape('Test conditionAsync', function (t) { | ||
var DT = new DTS({conditions:conditions}) | ||
DT.run(sampleTree, {random:64}, function(err, result) { | ||
t.equal('Leaf C', result.result) | ||
t.end() | ||
}) | ||
}) | ||
20661
649
106