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