Justin01
Justin01

Reputation: 288

Iterate through nested objects to form a string

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

Answers (3)

Arvind sharma
Arvind sharma

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

Nenad Vracar
Nenad Vracar

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

Nina Scholz
Nina Scholz

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

Related Questions