3

I have an expression like this:

{
  "type": "BinaryExpression",
  "operator": "OR",
  "left": {
    "type": "Literal",
    "value": 1,
    "raw": "1"
  },
  "right": {
    "type": "BinaryExpression",
    "operator": "AND",
    "left": {
      "type": "Literal",
      "value": 2,
      "raw": "2"
    },
    "right": {
      "type": "Literal",
      "value": 3,
      "raw": "3"
    }
  }
}

I want to convert this into the following in JS.

logic: 'OR'
filters: [
    {1}
    logic: 'AND'
    filters: [
        {2},
        {3}

    ]
]

i.e. When there is an operator, I will put into a logic variable of the Javascript object. After that, I will check if there is a left and right attribute. If any of them is a literal, I will just add to filters array of javascript object. If the left and the right attribute is a binary expression, then inside the javascript object I would repeat the above process.

I tried various approaches, but somehow I would miss something. So, I am asking here. :

var currentFilter = {
  'logic': null,
  filters: []
};
test(expression, currentFilter);

function test(expression, currentFilter) {
  while (expression.left != null && expression.right != null) {
    currentFilter.logic = expression.operator;
    if (expression.left.type === 'Literal') {
      currentFilter.filters.push = expression.left.value;
    } else if (expression.left.type === 'BinaryExpression') {
      test(expression.left, currentFilter);
    }

    if (expression.right.type === 'Literal') {
      currentFilter.filters.push = expression.right.value;
    } else if (expression.right.type === 'BinaryExpression') {
      test(expression.right, currentFilter);
    }
  }
}
3
  • 2
    The example ("the following in JS") is not valid javascript. Could you clarify what you're trying to do? Commented Feb 7, 2019 at 2:16
  • The expression is a JSON.stringify(expression) output. Initially, the whole expression will be a binary expression. In the whole expression, I put 'operator ' in currentFilter.logic variable. After that, In the expression, If the left attribute is Literal, I want to put it into currentFilter.filters array. If it is not a literal but a BinaryExpression, then I create an object of type currentFilter inside currentFilter.filters[] array as this array can hold elements of different type. Then I again check the operator and put it in currentFilter.filters['logic'] and keep on nesting. Commented Feb 7, 2019 at 2:29
  • Same for currentFilter.right Commented Feb 7, 2019 at 2:29

2 Answers 2

3

It looks like you are pretty close with your example, and are trying to use recursion to build your desired object. The reason why yours is failing is likely the while loop, as it will get into an infinite loop due to expression not being updated. Something like this should be more like what you are looking for:

const exp = {
  "type": "BinaryExpression",
  "operator": "OR",
  "left": {
    "type": "Literal",
    "value": 1,
    "raw": "1"
  },
  "right": {
    "type": "BinaryExpression",
    "operator": "AND",
    "left": {
      "type": "Literal",
      "value": 2,
      "raw": "2"
    },
    "right": {
      "type": "Literal",
      "value": 3,
      "raw": "3"
    }
  }
}

const parseValue = v => v.type === 'Literal' ? v.value : parseExp(v)

const parseExp = ({ operator, left, right }) => ({
  logic: operator,
  filters: [parseValue(left), parseValue(right)]
})

console.log(parseExp(exp))

Sign up to request clarification or add additional context in comments.

Comments

1

Consider a recursive approach -

const eval = (expr) =>
{ switch (expr.type)
  { case 'Literal':
      return expr.value
    case 'BinaryExpression':
      return apply
        ( expr.operator
        , eval (expr.left)
        , eval (expr.right)
        )
    default:
      throw Error (`invalid expression type: ${type}`)
  }
}

const apply = (op, l, r) =>
{ switch (op)
  { case 'AND':
      return l && r
    case 'OR':
      return l || r
    default:
      throw Error (`invalid operator: ${op}`)
  }
}

console.log(eval(e))
// 1 || (2 && 3)
// => 1

Expand the snippet below to verify the results in your own browser -

const eval = (expr) =>
{ switch (expr.type)
  { case 'Literal':
      return expr.value
    case 'BinaryExpression':
      return apply
        ( expr.operator
        , eval (expr.left)
        , eval (expr.right)
        )
    default:
      throw Error (`invalid expression type: ${type}`)
  }
}

const apply = (op, l, r) =>
{ switch (op)
  { case 'AND':
      return l && r
    case 'OR':
      return l || r
    default:
      throw Error (`invalid operator: ${op}`)
  }
}

const e =
  { type: "BinaryExpression"
  , operator: "OR"
  , left:
      { type: "Literal"
      , value: 1
      , raw: "1"
      }
  , right:
      { type: "BinaryExpression"
      , operator: "AND"
      , left:
          { type: "Literal"
          , value: 2
          , raw: "2"
          }
      , right:
          { type: "Literal"
          , value: 3
          , raw: "3"
          }
      }
  }


console.log(eval(e))
// 1 || (2 && 3)
// => 1

Now hopefully you can see how to build the result you want. This is left an exercise for the reader.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.