Не могу правильно сформировать массив JS
У меня есть функционал для решения логических уравнений. Выглядит это примерно так :
- Кнопка (добавить ПАРАМЕТР)
- Кнопка ("("),
- Кнопка (")"),
- Кнопка ("И"),
- Кнопка ("ИЛИ")
Кнопка Параметр включает в себя сравнение двух переменных (если val_id_first > val_id_second пример)
По клику на каждую из кнопок в пустой блок вставляется та или иная логическая операция. В конце я получаю json массив такого характера
{
0:{ logic: "(" },
1:{ id_one: "val_id_first",compare: " > ", id_two: "val_id_second" },
2:{ logic: "&&" },
3:{ id_one: "val_id_first",compare: " < ", id_two: "val_id_second" },
4:{ logic: ")" }
}
Все идет по порядку нажатия на то или иное действие. Вопрос, как мне получить в одну строку все эти действия, с тем учетом, что бы я смог мои val_id_first и val_id_second использовать как ключ другого массива, data['val_id_second '] в последующих действиях. Буду рад любой помощи. Спасибо.
Ответы (3 шт):
const operators = [
{logic: "("},
{id_one: "val_id_first", compare: " > ", id_two: "val_id_second"},
{logic: "&&"},
{id_one: "val_id_first", compare: " < ", id_two: "val_id_second"},
{logic: ")"}
];
const data = {
"val_id_first": 10,
"val_id_second": 20
};
function calcExp(exp) {
const val1 = data[exp.id_one];
const val2 = data[exp.id_two];
switch (exp.compare) {
case ' > ': return val1 > val2;
case ' >= ': return val1 >= val2;
case ' < ': return val1 < val2;
case ' <= ': return val1 <= val2;
case ' == ': return val1 == val2;
}
}
function calc(ops, start) {
const res = {
val: undefined,
end: undefined
};
let op;
for (let idx = start; idx < ops.length; idx++) {
let curVal;
switch (ops[idx].logic) {
case '(':
const obj = calc(ops, idx + 1);
curVal = obj.val;
idx = obj.end;
break;
case ')':
res.end = idx;
return res;
case '&&':
case '||':
op = ops[idx].logic;
break;
default:
curVal = calcExp(ops[idx]);
}
if (curVal !== undefined) {
switch (op) {
case '&&':
res.val = res.val && curVal;
break;
case '||':
res.val = res.val || curVal;
break;
default:
res.val = curVal;
}
op = undefined;
}
}
return res;
}
console.log(calc(operators, 0).val);
Пример создания из объекта (в вопросе) либо строки выражения, либо функции (для того чтобы результат выражения можно было вычислить, подставив в него значения операндов):
const createCalcFunc = (exprObj, option) => {
option = String(option).trim().toLowerCase();
const returnExpressionOnly = (option === 'expression'),
useArgsObject = (option === 'argsobject');
let parameters = new Set();
const expression = Object.values(exprObj).map(part => {
const tokens = [];
for (let [key, token] of Object.entries(part)) {
token = token.trim();
if (/^id_/i.test(key)) { // если токен выражения является операндом
if (useArgsObject)
token = `args.${token}`;
else
parameters.add(token);
}
tokens.push(token);
}
return tokens.join(' ');
}).join(' ');
if (returnExpressionOnly) return expression;
if (useArgsObject) return new Function('args', `return ${expression};`);
return new Function(...parameters, `return ${expression};`);
};
const testExpressionObj = {
0: { logic: "(" },
1: { id_one: "val_id_first", compare: " > ", id_two: "val_id_second" },
2: { logic: "&&" },
3: { id_one: "val_id_first", compare: " < ", id_two: "val_id_third" },
4: { logic: ")" },
};
// 1. обычное использование этого "генератора функций", без опции - операнды выражения передаются параметрами вызова (в порядке их первого упоминания в выражении)
const testFunc = createCalcFunc(testExpressionObj);
console.log('1a:', testFunc(8, 4, 16)); // --> (8 > 4 && 8 < 16) --> true
console.log('1b:', testFunc(32, 4, 16)); // --> (32 > 4 && 32 < 16) --> false
// 2. опция 'expression' - только получение выражения, без создания функции (для отладки выражений, или их вывода в UI)
const testExpr = createCalcFunc(testExpressionObj, 'expression');
console.log('2:', testExpr); // --> "( val_id_first > val_id_second && val_id_first < val_id_third )"
// 3. опция 'argsObject' - для передачи функции операндов выражения в виде объекта (имена свойств которого соответствуют именам "переменных" в выражении)
const argsTestFunc = createCalcFunc(testExpressionObj, 'argsObject');
const args = { val_id_first: 9, val_id_second: 5, val_id_third: 42 };
console.log('3:', argsTestFunc(args)); // --> (9 > 5 && 9 < 42) --> true
В примере я изменил объект с частями выражения добавив третью переменную - и чтобы поинтереснее было, и потому что выражение из вопроса содержит логическую ошибку (из-за которой оно всегда возвращало бы false).
Несущественное:
Если зачем-то хочется получить на выходе массив из одного элемента (как указано в вопросе), то
const arr = [createCalcFunc(/*....*/)];
А если в режиме возврата строки выражения не нравятся (добавляемые для надежности) пробелы вокруг скобок, то
return expression.replace(/\s*([()])\s*/g, '$1');
В моем случае это сработало - спасибо @yar85 Я отличаю как бы это глупо не звучало операторы присваивания по длине xD Ах да, в результате я ловлю true или false как
const logical_condition = this.logicCondition(val, rowNode.data);
console.log(eval(logical_condition));
function logicCondition(val, rowData) {
return val.map(part => {
const tokens = [];
for (const [key, token] of Object.entries(part)) {
if (token.length > 2) {
tokens.push(rowData[token]);
} else {
switch (token) {
case '=' :
tokens.push('===');
break;
case '<>' :
tokens.push('!==');
break;
default:
tokens.push(token);
}
}
}
return tokens.join(' ');
}).join(' ');
}