Reputation: 288
I have the following array, containing multiple nested objects (the nesting level could be infinite) :
var logicRules = [
{
'operator' : null,
'conditionString' : 'x == y'
},
{
'operator' : 'and',
'conditionString' : 'x > y'
},
{
'operator' : 'or',
'rules' : [
{
'operator' : null,
'conditionString' : 'x <= y'
},
{
'operator' : 'and',
'conditionString' : 'x > y'
},
{
'operator' : 'and',
'rules' : [
{
'operator' : null,
'conditionString' : 'x < y'
},
{
'operator' : 'or',
'conditionString' : 'x != y'
}
]
},
{
'operator' : 'and',
'conditionString' : 'x == y'
}
]
},
{
'operator' : 'and',
'conditionString' : 'x >= y'
}
];
If I perform processCondition(logicRules)
(see function below), the goal is to end up with a string like this :
if(x == y && x > y || (x <= y && x > y && (x < y || x != y)) && x >= y){ doSomething(); }
I will later eval() this string. And yes many front-end and back-end precautions are taken to make sure the eval() is safe to execute.
Below is my latest effort to achieve my goal. And yes I'm aware it is completely off in some parts. I just can't figure out how to solve it and it's driving me nuts.
function processCondition(rules){
var fullConditionString = "if(";
_processConditionsRecursive(rules);
function _processConditionsRecursive(logicRules, isGrouped){
var groupedConditionString = (typeof isGrouped != "undefined" && isGrouped) ? "(" : "";
for (var key in logicRules) {
if (logicRules.hasOwnProperty(key)) {
if(typeof logicRules[key].rules != "undefined" && logicRules[key].rules.length){
groupedConditionString += '(' + _processLogicRulesRecursive(logicRules[key].rules, true) + ')';
}else{
groupedConditionString += " " + logicRules[key].conditionString + " ";
}
}
}
groupedConditionString += (typeof isGrouped != "undefined" && isGrouped) ? ")" : "";
fullConditionString += groupedConditionString;
return groupedConditionString;
}
fullConditionString += '){ doSomething(); }';
return fullConditionString;
}
(!) I can do the operator separation myself no problem. Right now I'm mostly just worried about grouping conditions in brackets.
Thank you so very much!
Upvotes: 1
Views: 980
Reputation: 52
Hey Justin,
I have written a recursive method to resolve your issues. Please compare your
code with mine. I hope this resolves your issue.
/* Recursive generator function */
function logicGenerator(logicRules, str) {
/* Checking whether rules are there */
if (logicRules.length) {
/* Looping through each rule */
for (const rule of logicRules) {
switch (rule.operator) {
case null:
str += ` (`;
break;
case "and":
str += ` &&`;
break;
case "or":
str += `||`;
break;
}
/* Adding the expression to the string */
if (rule.conditionString) str += ` ${rule.conditionString} `;
/* If there are rules available then calling the method recursively and
passing the string generated so far */
if (rule.rules && rule.rules.length)
str = ` ${logicGenerator(rule.rules, str)}`;
}
}
// Adding the close parenthesis
str += `)`;
return str;
}
/* Wrapper function */
function generateLogicString(logicRules) {
return logicGenerator(logicRules, "");
}
Runner method
const result = eval(generateLogicString(logicRules))
-----------------------------
I have tested the above code with below long input and it is working fine
var logicRules = [
{
operator: null,
rules: [
{
operator: null,
conditionString: "x <= y"
},
{
operator: "and",
conditionString: "x > y"
},
{
operator: "and",
rules: [
{
operator: null,
conditionString: "x < y"
},
{
operator: "or",
conditionString: "x != y"
}
]
},
{
operator: "and",
conditionString: "x == y"
}
]
},
{
operator: "and",
conditionString: "x > y"
},
{
operator: "or",
rules: [
{
operator: null,
conditionString: "x <= y"
},
{
operator: "and",
conditionString: "x > y"
},
{
operator: "and",
rules: [
{
operator: null,
conditionString: "x < y"
},
{
operator: "or",
rules: [
{
operator: null,
conditionString: "x <= y"
},
{
operator: "and",
conditionString: "x > y"
},
{
operator: "and",
rules: [
{
operator: null,
conditionString: "x < y"
},
{
operator: "or",
rules: [
{
operator: null,
conditionString: "x <= y"
},
{
operator: "and",
conditionString: "x > y"
},
{
operator: "and",
rules: [
{
operator: null,
rules: [
{
operator: null,
conditionString: "x <= y"
},
{
operator: "and",
rules: [
{
operator: null,
conditionString: "x <= y"
},
{
operator: "and",
conditionString: "x > y"
},
{
operator: "and",
rules: [
{
operator: null,
conditionString: "x < y"
},
{
operator: "or",
conditionString: "x != y"
}
]
},
{
operator: "and",
conditionString: "x == y"
}
]
},
{
operator: "and",
rules: [
{
operator: null,
conditionString: "x < y"
},
{
operator: "or",
conditionString: "x != y"
}
]
},
{
operator: "and",
conditionString: "x == y"
}
]
},
{
operator: "or",
conditionString: "x != y"
}
]
},
{
operator: "and",
conditionString: "x == y"
}
]
}
]
},
{
operator: "and",
conditionString: "x == y"
}
]
}
]
},
{
operator: "and",
conditionString: "x == y"
}
]
},
{
operator: "and",
conditionString: "x >= y"
}
];
Thanks
Upvotes: 0
Reputation: 122047
You could do this using recursive function with reduce
method.
var rules = [{"operator":null,"conditionString":"x == y"},{"operator":"and","conditionString":"x > y"},{"operator":"or","rules":[{"operator":null,"conditionString":"x <= y"},{"operator":"and","conditionString":"x > y"},{"operator":"and","rules":[{"operator":null,"conditionString":"x < y"},{"operator":"or","conditionString":"x != y"}]},{"operator":"and","conditionString":"x == y"}]},{"operator":"and","conditionString":"x >= y"}]
function process(rules) {
return rules.reduce((r, e, i) => {
let nested = ''
let op = '';
let cond = '';
if (e.rules) {
nested = process(e.rules);
}
if (e.conditionString) {
cond = e.conditionString
}
if(i === 0) op = '';
else if (e.operator === 'and') op = '&&';
else if (e.operator === 'or') op = '||';
r += (op ? ` ${op} ` : '') + cond + (nested ? `(${nested})` : '')
return r;
}, '')
}
const result = process(rules);
console.log(result)
Upvotes: 1
Reputation: 386634
You could reduce the arrays by looking to the first item and omit any operator and by checking if rules
exists, then take the nested elements or the condition.
function combine(r, { operator, conditionString, rules }, i) {
var ops = { and: '&&', or: '||' };
return r + (i ? ` ${ops[operator]} ` : '') + (rules
? `(${rules.reduce(combine, '')})`
: conditionString
);
}
var logicRules = [{ operator: null, conditionString: "x == y" }, { operator: "and", conditionString: "x > y" }, { operator: "or", rules: [{ operator: null, conditionString: "x <= y" }, { operator: "and", conditionString: "x > y" }, { operator: "and", rules: [{ operator: null, conditionString: "x < y" }, { operator: "or", conditionString: "x != y" }] }, { operator: "and", conditionString: "x == y" }] }, { operator: "and", conditionString: "x >= y" }],
result = logicRules.reduce(combine, '');
console.log(result);
Upvotes: 1