var list = "OVER_30 = true || NUM_OF_JACKETS >=3 || COUNT_TOTAL == 500";
var array = getList(); // array[0].OVER_30 = true, array[0].NUM_OF_JACKETS = 5, array[0].COUNT_TOTAL = 500;
if (array[0].OVER_30 = true || array[0].NUM_OF_JACKETS >=3 || array[0].COUNT_TOTAL == 500) { <--- What I want to accomplish
return true;
}
I have a string variable called list that contains the conditions. How can I add array[0]. in front of each condition to combine the array and string?
var format = array[0]. + condition??
I could only find eval for you
You DO need to make sure the statements are correctly formatted with spaces and correct number of =
So here I use two maps and a some
- if you have && instead of || you need every
which will return one combined result, meaning [true,true,true] is true and any other combination is false
Using some
will return one combined result, any true is true
const list = "OVER_30 === true || NUM_OF_JACKETS >= 3 || COUNT_TOTAL === 500"; const array = [ { OVER_30 : true, NUM_OF_JACKETS : 5, COUNT_TOTAL : 500}, { OVER_30: false, NUM_OF_JACKETS: 2, COUNT_TOTAL: 400 }, { OVER_30: true, NUM_OF_JACKETS: 2, COUNT_TOTAL: 400 } ] const tests = list.split("||"); const results = array.map(item => tests.map(test => { const [name, oper, val] = test.trim().split(" "); const statement = `${item[name]} ${oper} ${val}` return eval(statement) }).some(test => test) ) console.log(results)
You still need eval
, but you could destructure the object and perform eval
.
This approach need a valid check with a comparison operator
OVER_30 == true
instead of an assignment operator with =
.
const array = [ { OVER_30: true, NUM_OF_JACKETS: 5, COUNT_TOTAL: 500 }, { OVER_30: false, NUM_OF_JACKETS: 2, COUNT_TOTAL: 400 } ], list = "OVER_30 == true || NUM_OF_JACKETS >=3 || COUNT_TOTAL == 500", result = array.map(({ OVER_30, NUM_OF_JACKETS, COUNT_TOTAL }) => eval(list)); console.log(result);
Change your operator from =
(assignment) to ==
or ===
(comparison) for the first condition. Or just use OVER_30
. Hence:
"OVER_30 || NUM_OF_JACKETS >=3 || COUNT_TOTAL === 500"
You can use new Function()()
constructor and Array#some()
method as follows:
list.split('||').some(cond => new Function(`return array[0].${cond}`)())
which returns true
if at least one of the conditions is true.
var list = "OVER_30 || NUM_OF_JACKETS >=3 || COUNT_TOTAL === 500"; var array = [ {OVER_30:true, NUM_OF_JACKETS:5, COUNT_TOTAL:500}, {OVER_30:true, NUM_OF_JACKETS:0, COUNT_TOTAL:100}, {OVER_30:false, NUM_OF_JACKETS:2, COUNT_TOTAL:200}, {OVER_30:false, NUM_OF_JACKETS:1, COUNT_TOTAL:400} ]; for(let i in array) { if( list.split('||').some(cond => new Function(`return array[${i}].${cond}`)()) ) { console.log( 'returning true.' ); } else { console.log( '....false!' ); } }; list.split(' || ').forEach(cond => console.log( new Function(`return array[1].${cond}`)() ));
An approach without using eval
but instead a simple parser and then evaluation:
//helper functions const peek = arr => arr[arr.length - 1]; const isOperator = token => typeof token === "object"; //available operators const operators = new Map([ [ "===", { precedence: 2, operation: (a, b) => a === b }], [ ">=", { precedence: 2, operation: (a, b) => a >= b }], [ "||", { precedence: 1, operation: (a, b) => a || b }], ]); //convert into operators and operands const tokenize = str => str.split(/\\s+|\\b/) .map(token => operators.has(token) ? operators.get(token) : token ); //convert literal tokens and binary operators into reverse polish notation const parse = tokens => { const opStack = []; const output = []; for (const token of tokens) { if (isOperator(token)) { while(isOperator(peek(opStack)) && token.precedence <= peek(opStack).precedence) { output.push(opStack.pop()); } opStack.push(token); } else { output.push(token); } } return output.concat(opStack.reverse()); }; const consume = (rpnTokens, obj) => { const output = []; for(const token of rpnTokens) { if (isOperator(token)) { const b = output.pop(); const a = output.pop(); const result = token.operation(a, b); output.push(result); } else { const value = token in obj ? obj[token] //object properties - fetch from object : JSON.parse(token); //others convert into values output.push(value); } } return output[0]; } // ▲▲▲ code ▲▲▲ // ▼▼▼ usage ▼▼▼ const list = "OVER_30 === true || NUM_OF_JACKETS >= 3 || COUNT_TOTAL === 500"; const array = [ { OVER_30: true, NUM_OF_JACKETS: 7, COUNT_TOTAL: 500 }, //true { OVER_30: false, NUM_OF_JACKETS: 7, COUNT_TOTAL: 500 }, //true { OVER_30: false, NUM_OF_JACKETS: 1, COUNT_TOTAL: 500 }, //true { OVER_30: false, NUM_OF_JACKETS: 1, COUNT_TOTAL: 100 }, //false { OVER_30: true, NUM_OF_JACKETS: 1, COUNT_TOTAL: 100 } //true ]; const tokens = tokenize(list); const rpn = parse(tokens); for (const item of array) { console.log(consume(rpn, item)); }
"OVER_30 === true || NUM_OF_JACKETS >= 3 || COUNT_TOTAL === 500"
will be represented as[ "OVER_30", OP[===], "true", OP[||], "NUM_OF_JACKETS", OP[>=], "3", OP[||], "COUNT_TOTAL", OP[===], "500" ]
[ "OVER_30", "true", OP[===], "NUM_OF_JACKETS", "3", OP[>=], OP[||], "COUNT_TOTAL", "500", OP[===], OP[||] ]
The list is consumed by going through it one by one.
3.1. Any operand is converted to a value and added to a stack:
3.2. Operators are applied to the last two values and the result is put on the stack.
In the end, there is a single result on the stack and that is the result of the evaluation of all tokens.
This assumes a well formed and logical string is given.
The solution can now handle any valid expression and can work with literals. It can be extended with more operators by just adding them to the configuration:
//helper functions const peek = arr => arr[arr.length - 1]; const isOperator = token => typeof token === "object"; //available operators const operators = new Map([ [ "+", { precedence: 3, operation: (a, b) => a + b }], //support addition [ "===", { precedence: 2, operation: (a, b) => a === b }], [ ">=", { precedence: 2, operation: (a, b) => a >= b }], [ "||", { precedence: 1, operation: (a, b) => a || b }], ]); //convert into operators and operands const tokenize = str => str.split(/\\s+|\\b/) .map(token => operators.has(token) ? operators.get(token) : token ); //Shunting-yard algorithm for literal tokens and binary operators into reverse polish notation const parse = tokens => { const opStack = []; const output = []; for (const token of tokens) { if (isOperator(token)) { while(isOperator(peek(opStack)) && token.precedence <= peek(opStack).precedence) { output.push(opStack.pop()); } opStack.push(token); } else { output.push(token); } } return output.concat(opStack.reverse()); }; const consume = (rpnTokens, obj) => { const output = []; for(const token of rpnTokens) { if (isOperator(token)) { const b = output.pop(); const a = output.pop(); const result = token.operation(a, b); output.push(result); } else { const value = token in obj ? obj[token] //object properties - fetch from object : JSON.parse(token); //others convert into values output.push(value); } } return output[0]; } console.log(consume(parse(tokenize("5 >= 6")), {})); //false console.log(consume(parse(tokenize("7 >= 6")), {})); //true console.log(consume(parse(tokenize("4+1>=6")), {})); //false console.log(consume(parse(tokenize("4+3>=6")), {})); //true console.log(consume(parse(tokenize("1+1+1 === 3")), {})); //true console.log(consume(parse(tokenize("true || false")), {})); //true console.log(consume(parse(tokenize("false || false")), {})); //true console.log(consume(parse(tokenize("apples === oranges+2")), //true { apples: 42, oranges: 40}) );
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.