For business reasons we need to externalize some conditional logic into external files: preferably JSON.
A simple filter-by scenario could be handled by adding a node a
The first came to mind would be the recurisve
dict1={'$lte':'<','$nin':'not in '}
def updateOp(subdictItem):
for ites in subdictItem:
ops = ites
print dict1.get(ops), subdictItem.get(ops), type(subdictItem.get(ops))
if type(subdictItem.get(ops)) is list:
valuelist=subdictItem.get(ops)
strlist=' ,'.join([str(x) for x in valuelist])
sub = dict1.get(ops) + "(" +strlist +")"
else:
sub = dict1.get(ops) +' ' + str(subdictItem.get(ops))
return sub
def jsonString(input_dict):
items=''
itemslist=[]
list = []
for item in input_dict:
op=item
itemssublist=[]
# print "item",op
for subitem in input_dict.get(op):
# print("subitem",subitem)
for ite in subitem:
if ite not in ('and','or'):
# print('ite_raw',ite,subitem.get(ite))
sub=''
if type(subitem.get(ite)) is dict:
sub=updateOp(subitem.get(ite))
else:
sub='=' + str(subitem.get(ite))
itemssublist.append(ite+sub)
else:
item1=jsonString(subitem)
itemssublist.append(item1)
delimiter=" "+op+ " "
items= "("+delimiter.join(itemssublist)+")"
return items
if __name__ == "__main__":
input_dict={}
with open('ops.json','r') as f:
input_dict=json.load(f)
print input_dict
test= jsonString(input_dict)
#result : (age< 3 or name=Joe or (age=5 and age not in (1 ,2 ,3)))
ops.json file:
{
"or":[
{
"age":{
"$lte":3
}
},
{
"name":"Joe"
},
{
"and":[
{
"age":5
},
{
"age ":{
"$nin":[
1,
2,
3
]
}
}
]
}
]
}
If you must implement this using standard JSON, i'd recommend something akin to Lisp's "S-expressions". A condition could be either a plain object, or an array whose first entry is the logical operation that joins them.
For example:
["AND",
{"var1" : "value1"},
["OR",
{ "var2" : "value2" },
{ "var3" : "value3" }
]
]
would represent var1 == value1 AND (var2 == value2 OR var3 == value3)
.
If you prefer brevity over consistency, you could also allow an object to have multiple properties, which would implicitly be joined by an AND. For example, { "a": "b", "c": "d" }
would be equivalent to ["AND", { "a": "b" }, { "c": "d" }]
. But there are cases (like the example) where the former syntax can not faithfully represent the condition as written; you'd need additional trickery like translating the condition or using dummy property names. The latter syntax should always work.
Following Jeremy Wadhams comment, I implemented a parser I hope it can help you:
https://play.golang.org/p/QV0FQLrTlyo
The idea is to set all logic operators in special keys with $
character like $and
or $lte
.
As an example:
{
"$or":[
{
"age":{
"$lte":3
}
},
{
"name":"Joe"
},
{
"$and":[
{
"age":5
},
{
"age ":{
" $nin ":[
1,
2,
3
]
}
}
]
}
]
}
Regards.
Is translated as:
( age <= 3 OR name = Joe OR ( age = 5 AND age NOT IN (1,2,3) ) )
Formula parser + a bit of JS codes to put data into formulas, is another solution described with example in this answer.
I had a similar need (to build up a sql where clause in javascript). I create dthe following javascript function:
function parseQuery(queryOperation){
var query="";
if (queryOperation.operator == 'and')
query = "(" + parseQuery(queryOperation.leftOp) + ") AND (" + parseQuery(queryOperation.rightOp) + ")";
if (queryOperation.operator == 'or')
query = "(" + parseQuery(queryOperation.leftOp) + ") OR (" + parseQuery(queryOperation.rightOp) + ")";
if (queryOperation.operator == '=')
query = "(" + queryOperation.leftOp +" = "+ queryOperation.rightOp + ")";
return query;
}
I create my queryOperation Like this:
var queryObject = {
operator: 'and',
leftOp: {
leftOp: 'tradedate',
operator: '=',
rightOp: new Date()
},
rightOp: {
operator: 'or',
leftOp: {
leftOp: 'systemid',
operator: '=',
rightOp: 9
},
rightOp: {
leftOp: 'systemid',
operator: '=',
rightOp:10
}
}
};
When I pass my queryOperation to ParseQuery it returns ((tradedate= Thu Jul 24 17:30:37 EDT 2014)) AND (((systemid= 9)) OR ((systemid= 10)))
I need to add some type conversions and other operators, but the basic structure works.
I just wanted to help by defining a parsing logic in JavaScript for the JSON structure mentioned in the answer: https://stackoverflow.com/a/53215240/6908656
This would be helpful for people having a tough time in writing a parsing logic for this.
evaluateBooleanArray = (arr, evaluated = true) => {
if (arr.length === 0) return evaluated;
else if (typeof arr[0] === "object" && !Array.isArray(arr[0])) {
let newEvaluated = checkForCondition(arr[0]);
return evaluateBooleanArray(arr.splice(1), newEvaluated);
} else if (typeof arr[0] === "string" && arr[0].toLowerCase() === "or") {
return evaluated || evaluateBooleanArray(arr.splice(1), evaluated);
} else if (typeof arr[0] === "string" && arr[0].toLowerCase() === "and") {
return evaluated && evaluateBooleanArray(arr.splice(1), evaluated);
} else if (Array.isArray(arr[0])) {
let arrToValuate = [].concat(arr[0]);
return evaluateBooleanArray(
arr.splice(1),
evaluateBooleanArray(arrToValuate, evaluated)
);
} else {
throw new Error("Invalid Expression in Conditions");
}
};
So the param arr here would be an array of conditions defined in the format as described by the attached link.