-1
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??

  • You can probably use [`eval()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval) or parse the string – Cid Nov 20 '20 at 09:30
  • Are you familiar with the constructor for `Function` objects? They're shown in the third example of the accepted answer, here: https://stackoverflow.com/questions/7650071/is-there-a-way-to-create-a-function-from-a-string-with-javascript - Using such a constructor, one could parse the string you've provided (after corrections to syntax errors), before generating the string that would represent the function body that will produce the desired result. – enhzflep Nov 20 '20 at 10:23

4 Answers4

1

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)
mplungjan
  • 169,008
  • 28
  • 173
  • 236
1

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);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

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}`)() ));
PeterKA
  • 24,158
  • 5
  • 26
  • 48
0

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" 
    ]
    
  2. 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[||] 
    ]
    
  3. 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 JavaScript 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")), {})); //false
console.log(consume(parse(tokenize("apples === oranges+2")), //true
  { apples: 42, oranges: 40})
);
VLAZ
  • 26,331
  • 9
  • 49
  • 67