Tengo dos matrices, una que contiene booleanos y los otros operadores:
to_eval = [true, true, false, false] ops=['&&', '||', '&&']A partir de esto me gustaría construir una expresión
result = true && true || false && falseQue debería evaluarse como verdadero según las reglas de precedencia
Si itero sobre las matrices que se evalúan a medida que avanzo, el resultado es falso.
Podría construir una cadena y usar Eval o Function, pero por lo que he estado leyendo, esto puede causar problemas de seguridad en una aplicación web.
¿Hay alguna forma en Javascript de construir una expresión sin evaluarla hasta que esté completamente formada para que se observen las reglas de precedencia sin usar Eval o Function?
Puede crear funciones de mapeo para los operadores lógicos de confianza de forma segura con Array#reduce :
Pero la forma en que esto se evalúa de izquierda a derecha sin precedencias, es
true && true || false && falseevaluaráfalse. Si desea un resultado más preciso, como la forma en que JavaScript analiza las cosas, sería más fácil usarevalcon la desinfección adecuada.
const to_eval = [true, true, false, false]; const ops = ['&&', '||', '&&']; const ops_map = { '&&': (a, b) => a && b, '||': (a, b) => a || b }; const result = to_eval.reduce((acc, cur, i) => ops_map[ops[i-1]](acc, cur)); console.log(result); Con la santización adecuada, puede usar eval de manera segura:
const evaluate = (to_eval, ops = []) => { const expression = to_eval.reduce((acc, cur, i) => { let left = i === 1 ? !!acc : acc; let op = {'&&': '&&', '||': '||'}[ops[i-1]]; if(typeof op !== 'string') { op = '||'; // fallback } let right = !!cur; return `${left} ${op} ${right}` }); const result = eval(expression); console.log(expression, ' => ', result); return result; } const to_eval = [true, true, false, false] const ops = ['&&', '||', '&&']; evaluate(to_eval, ops); evaluate([true], []); evaluate(['alert(1)', 0, false], ['alert(2)', 'hasOwnProperty']);Puede llamar con seguridad a eval , si está seguro de que sus dos matrices tienen los valores esperados (booleanos y operadores esperados). Así que solo agregue algo de código para verificar las dos entradas dadas.
Puedes hacer lo siguiente:
function evaluate(bools, ops) { // Verify the given arguments are as expected if (!ops.every(op => ["&&", "||"].includes(op))) throw "invalid operator"; if (!bools.every(bool => typeof bool === "boolean")) throw "invalid operand"; if (bools.length !== ops.length + 1) throw "length mismatch"; return eval(bools.map((bool, i) => bool + " " + (ops[i] ?? "")).join(" ")); } let to_eval = [true, true, false, false]; let ops = ['&&', '||', '&&']; let result = evaluate(to_eval, ops); console.log(result);Podría usar eval si desinfecta la entrada (es decir, verifique que todos los tokens sean de una lista muy específica de tokens permitidos).
function safeEval(expression) { const allowed = ['true', 'false', '&&', '||']; if (expression.split(/\s+/).every(token => allowed.includes(token))) { return eval(expression); } else { throw Error('Encountered forbidden token.'); } } console.log(safeEval('true && true || false && false')); console.log(safeEval('undefined'));Esto está lejos de ser el código más eficiente que podría escribir, pero es simple y hace el trabajo, y agregar soporte para otros operadores y paréntesis es trivial.
Alternativamente, puede evaluar los operadores usted mismo, en el orden de precedencia que desee:
const expression = [true, '&&', true, '||', false, '&&', false]; for (let i = 0; i < expression.length; ++i) { if (expression[i] === '&&') { const left = expression[i - 1]; const right = expression[i + 1]; expression.splice(i - 1, 3, left && right); } } console.log(expression); for (let i = 0; i < expression.length; ++i) { if (expression[i] === '||') { const left = expression[i - 1]; const right = expression[i + 1]; expression.splice(i - 1, 3, left || right); } } console.log(expression);Probablemente querrá hacer que este código sea más sólido y menos repetitivo si agregara soporte para muchos operadores, pero al menos eso debería ayudarlo a comenzar.