简体   繁体   中英

JavaScript - Get an array object from a string variable

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)); }

  1. The string is converted into tokens "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" ]
  1. Afterwards a simplified version of the shunting-yard algorithm re-orders the tokens into reverse Polish notation producing:
[ "OVER_30", "true", OP[===], "NUM_OF_JACKETS", "3", OP[>=], OP[||], "COUNT_TOTAL", "500", OP[===], OP[||] ]
  1. 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:

    • something that exists on an object as a property is converted by getting the value from the object
    • anything else is assumed to be a JavaString primitive and converted to a value

    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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM