swiftnsexpression

NSExpression Swift from String with IF statement


Currently using NSExpression in swift to take a string formula from a database and calculate it.

Have a range of inputs (buttons / textfields etc) that when inputted hold an array of values for each input. Then I have a string formula held in a database with '$' as identification of each value.

Example Inputs in array may be: 5,3,2,6 String formula is: (($*$)-$)/$ Code then substitutes the $ in order to the input array so produces string: ((5*3)-2)/6 This is used by NSExpression to calculate. Works fine.

 let formulaArray = formula.components(separatedBy: "$")
                var totalScoreFormula = ""
                
                let sep = 0.0
                let explandedScoreArrayDouble = Array(scoreArrayDouble.map { [$0] }.joined(separator: [sep]))
                
                for i in 0..<formulaArray.count {
                    
                    if (i == 0){totalScoreFormula = "(" + String(explandedScoreArrayDouble[i])}
                    else if (formulaArray[i] == "") {totalScoreFormula = totalScoreFormula + String(explandedScoreArrayDouble[i])}
                    else {totalScoreFormula = totalScoreFormula + formulaArray[i]}
                    
                }
                let mathExpression = NSExpression(format: totalScoreFormula)
                totalScoreDouble = (mathExpression.expressionValue(with: nil, context: nil) as? Double)!

But I have more complicated formula that I would like to use that in essence contain IF statements, for example: If first in array value = 1 then formula is (($*$)-$)/$ however if value = 2 then formula should be (($*$)+$)*$

I would like to hold this IF statement in the string on the database and not in the code.

So for example database string something like: IF $==1: use(($*$)-$)/$, ELSE (($*$)+$)*$

Also I have a few formula that contain multiple IF or CASE aspects: For example: If Age <5 then this formula for male and this for female. If >5 then this this formula for male and this for female....etc

Wondered if use of TERNARY may be answer: NSExpression(format: "TERNARY($ == 1, '(($*$)-$)/$', '(($*$)+$)*$')")

Struggling to get this to work and can't see how I would do nestled calculations via one string.

Any advice, different approach?


Solution

  • The NSExpression format string works with property names, like NSPredicate. The property names are used to get the values from the object against which the expression is evaluated. This object can be a dictionary (or a KVC compliant class). So instead of an array of values and lots of $s, use a dictionary and keys. This way you can use a value multiple times. The formulas are longer but they are more readable. Example:

    let values1 = ["MALE": true, "AGE": 8, "POINTS": 5, "BONUS": 10] as [String : Any]
    let values2 = ["MALE": false, "AGE": 9, "POINTS": 8, "BONUS": 5] as [String : Any]
    let values3 = ["MALE": true, "AGE": 3, "POINTS": 11, "BONUS": 0] as [String : Any]
    
    let expression = NSExpression(format: "TERNARY(AGE > 5, TERNARY(MALE = TRUE, POINTS * 2, POINTS * 2 + 1), POINTS * 3)")
    
    print("Result 1: \(expression.expressionValue(with: values1, context: nil))")
    print("Result 2: \(expression.expressionValue(with: values2, context: nil))")
    print("Result 3: \(expression.expressionValue(with: values3, context: nil))")
    

    prints

    Result 1: Optional(10)
    Result 2: Optional(17)
    Result 3: Optional(33)
    

    Note: If the expression format is incorrect then NSExpression(format:) will throw an Objective-C NSException.