Evaluating each required variable in order from expression

I am currently using the golang package github.com/Knetic/govaluate to evaluate formulas from database. Here is an example of a formula I am using in my database

expression, err := govaluate.NewEvaluableExpression("totalCost / .85"); // for markup

parameters := make(map[string]interface{})
parameters["cost"] = 9.99;
parameters["qty"] = 2;
parameters["markup"] = nil // totalCost / .85 (this formula above)
parameters["handlingCharge"] = nil // if(cost > 100, 25, cost * 0.1). 
parameters["totalCost"] = nil // (cost * qty) + handlingCharge. 

result, err := expression.Evaluate(parameters);

handlingCharge and totalCost are also formulas. There is no limit (except circular references) on formulas containing other formula fields. Think of it like excel formula referencing another formula field.

Instead of coding x amount of for loops to check if each required variable field has another variable field, how can I effectually work each required formula?

// expressions map
var expressionsMap = make(map[string]*govaluate.EvaluableExpression)
var requiredVariables = make(map[string][]string)

// for each table field
for publicID, formula := range fieldsMap {

	// get expression into map
	if formula != "" {
		expression, err := govaluate.NewEvaluableExpressionWithFunctions(formula, functions)
		if err != nil {
			log.Println(err)
			return nil, errInternalServerError
		}

		// save expression to expressions map
		expressionsMap[publicID] = expression

        // save required variables to a map of required variables
		requiredVariables[publicID] = unique(expression.Vars())
	}
}

For this simple example requiredVariables would be

requiredVariables['handlingCharge'] = ['cost']
requiredVariables['markup'] = ['totalCost']
requiredVariables['totalCost'] = ['cost', 'qty', 'handlingCharge']

In total markup requires totalCost and totalCost needs handlingCharge. How can I programmatically calculate these formulas? Thank you for the help.

Are you asking how to track the “leaf” nodes of an expression or how to identify them, i.e., do you already know each expression’s direct requirements (which may themselves be expressions with their own parameters) or are you trying to figure out what those requirements are? If the latter, I think you’d have to check the methods or fields in the returned expression object to see if it has what you need. If the former, I think you have loop through the expression’s dependency graph, but if the expressions are immutable and reference each other by value or by reference (as long as it’s not by name), then you could do this at the time the expression is compiled:

var flattenedParams = make(map[string][]string)

func flattenParams(name string, directParams ...string) []string {
    if ps, ok := flattenedParams[name]; ok {
        return ps
    }
    out := make([]string, 0, 2*cap(directParams)) // kinda arbitrary cap.
    for _, param := range directParams {
    deps:
        for _, dep := range flattenParams(param) {
            for _, o := range out {
                if o == dep {
                    continue deps
                }
            }
            out = append(out, dep)
        }
    }
    flattenedParams[name] = out
    return out
}

I probably missed something, but I hope the idea is reasonable: As long as you define the dependencies first, this function should be able to flatten an expression into all the parameters it depends on.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.