I need to create a ui that user will build up a formula. ie:
For one item formula is:
Cost * item / 100
For another item:
Item* 5 / 100
DarthVader!
There are a couple options here and it depends on your needs and whether you need something very complex or something just simple to understand and expand upon (maybe for academic purposes).
1) Let's start with simple, easy and customizable. I have created a class that meets the needs you've specified on your post, however it is very raw and should NOT be used in commercial projects without further testing and modification... You can easily pick it up and increment upon it if you want... It shows a simple way to achieve what you need. The code works nicely but does not take into account math priorities (such as parentheses or * over +). It needs to be adapted in order to do so... Code is below, it is commented and hopefully self explanatory:
public class DynamicFormula
{
/// <summary>
/// This simply stores a variable name and its value so when this key is found in a expression it gets the value accordingly.
/// </summary>
public Dictionary<string, double> Variables { get; private set; }
/// <summary>
/// The expression itself, each value and operation must be separated with SPACES. The expression does not support PARENTHESES at this point.
/// </summary>
public string Expression { get; set; }
public DynamicFormula()
{
this.Variables = new Dictionary<string, double>();
}
public double CalculateResult()
{
if (string.IsNullOrWhiteSpace(this.Expression))
throw new Exception("An expression must be defined in the Expression property.");
double? result = null;
string operation = string.Empty;
//This will be necessary for priorities operations such as parentheses, etc... It is not being used at this point.
List<double> aux = new List<double>();
foreach (var lexema in Expression.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries))
{
//If it is an operator
if (lexema == "*" || lexema == "/" || lexema == "+" || lexema == "-")
{
operation = lexema;
}
else //It is a number or a variable
{
double value = double.MinValue;
if (Variables.ContainsKey(lexema.ToLower())) //If it is a variable, let's get the variable value
value = Variables[lexema.ToLower()];
else //It is just a number, let's just parse
value = double.Parse(lexema);
if (!result.HasValue) //No value has been assigned yet
{
result = value;
}
else
{
switch (operation) //Let's check the operation we should perform
{
case "*":
result = result.Value * value;
break;
case "/":
result = result.Value / value;
break;
case "+":
result = result.Value + value;
break;
case "-":
result = result.Value - value;
break;
default:
throw new Exception("The expression is not properly formatted.");
}
}
}
}
if (result.HasValue)
return result.Value;
else
throw new Exception("The operation could not be completed, a result was not obtained.");
}
/// <summary>
/// Add variables to the dynamic math formula. The variable should be properly declared.
/// </summary>
/// <param name="variableDeclaration">Should be declared as "VariableName=VALUE" without spaces</param>
public void AddVariable(string variableDeclaration)
{
if (!string.IsNullOrWhiteSpace(variableDeclaration))
{
var variable = variableDeclaration.ToLower().Split('='); //Let's make sure the variable's name is LOWER case and then get its name/value
string variableName = variable[0];
double variableValue = 0;
if (double.TryParse(variable[1], out variableValue))
this.Variables.Add(variableName, variableValue);
else
throw new ArgumentException("Variable value is not a number");
}
else
{
//Could throw an exception... or just ignore as it not important...
}
}
}
Here is an example using the class above in a WPF application (can be used in any C# application)
private void btCalculate_Click(object sender, RoutedEventArgs e)
{
string expression = tboxExpression.Text; //"cost * item / 100" (IT MUST BE SEPARATED WITH SPACES!)
string variable1 = tboxVariable1.Text; //"item=10"
string variable2 = tboxVariable2.Text; //"cost=2.5"
DynamicFormula math = new DynamicFormula();
math.Expression = expression; //Let's define the expression
math.AddVariable(variable1); //Let's add the first variable
math.AddVariable(variable2); //Let's add the second variable
try
{
double result = math.CalculateResult(); //In this scenario the result is 0,25... cost * item / 100 = (2.5 * 10 / 100) = 0,25
//Console.WriteLine("Success: " + result);
tboxResult.Text = result.ToString();
}
catch(Exception ex)
{
//Console.WriteLine(ex.Message);
tboxResult.Text = ex.Message;
}
}
2) If you need something more robust and for most real life situations, you should definitively check out FLEE: http://flee.codeplex.com/wikipage?title=Examples&referringTitle=Home
This is a library made specifically for that and it supports several formulas! It may take sometime to see some examples and understanding how it works but it should get the job done without much work.
Hope it helps,
Luís Henrique Goll.
Check this fiddle you can improve the formula like you want
html
<form id="ula">
<h1>Insert your formula</h1>
<input type="text" placeholder="Es: a(b+c)/2" />
<input type="submit" value="Create form" />
</form>
css
body{font-family:arial,sans-serif;text-align:center}
input{padding:6px;border:1p solid #999;margin:10px auto}
js
$('form').on('submit',function(e){
e.preventDefault();
$(this).hide();
$('body').append($('<div />').hide().fadeIn(800));
var labDiv=$('div:first');
var varNames = [];
var formula=$('input').val().toString();
var varStr=formula.replace(/[^a-zA-Z]+/g, "");
$.each(varStr.split(''), function(i, el) {
if ($.inArray(el, varNames) === -1){
varNames.push(el);
labDiv.append('<input name="'+el+'" placeholder="'+el+' value.." /><br />');
}
});
labDiv.prepend('<h1>'+formula+'</h1>');
labDiv.append('<button id="newFormula">New formula</button><button id="calculate">Calculate</button>')
$('#calculate').on('click',function(e){
e.preventDefault();
var result=formula.replace(/\(/g,'*(').replace(RegExp(':','g'),'/');
for(var ct=0;ct<varNames.length;ct++){
result=result.replace(new RegExp(varNames[ct], 'g'),$('input[name='+varNames[ct]+']').val());
console.log(result)
};
labDiv.append('<h2>'+result.replace(/\*\(/g,'(')+'= <b>'+eval(result.replace(',','.'))+'</b></h2>');
});
$('#newFormula').one('click',function(e){
e.preventDefault();
labDiv.remove();
$('form#ula input:first').val('');
$('form#ula').fadeIn();
});
})
Since the question was tagged with jQuery I am assuming this is a web application. Unless there is a need to post the formula to the server and have it evaluated there using vanilla JavaScript should make your life so much easier. JavaScript is an dynamic, untyped and interpreted language which means you can dynamically construct your formula in a string and then have it evaluated by your browser's javascript engine.
The following example of code from w3cscools.com:
var x = 10;
var y = 20;
var a = eval("x * y")
would evaluate to 200.
If however you need to evaluate the formula on server side, check what options you have on running interpreted languages on C#. In java there is a Javascript runtime (Nashorn) backed in the JVM so one could easily evaluate JavaScript expression on server side.