none
Convert string to formula

    Question

  • Hi All,

                    I developing layered architecture project in asp.net using C Sharp. I am storing the formula in database as string. After selecting the string I need to change it as formula. Whether its possible.

    Example:

    Input : formula given by user  “(a+b)/c”  store this as string because it change byuser at any time for example it can be “c/(a*b)”.

    Now this formula will retrieve from database for calculation. Now another user will enter the values for parameter for “a”, “b” and “c” for above example.

    Now in the code I need calculate the result.  How?

    Thanks

    Prathap

    Wednesday, March 07, 2012 4:03 AM

Answers

  • This is how it should be done:

    public class StringToFormula
    {
    	private string[] _operators = { "-", "+", "/", "*","^"};
    	private  Func<double, double, double>[] _operations = {
    		(a1, a2) => a1 - a2,
    		(a1, a2) => a1 + a2,
    		(a1, a2) => a1 / a2,
    		(a1, a2) => a1 * a2,
    		(a1, a2) => Math.Pow(a1, a2)
    	};
    
    	public double Eval(string expression)
    	{
    		List<string> tokens = getTokens(expression);
    		Stack<double> operandStack = new Stack<double>();
    		Stack<string> operatorStack = new Stack<string>();
    		int tokenIndex = 0;
    
    		while (tokenIndex < tokens.Count) {
    			string token = tokens[tokenIndex];
    			if (token == "(") {
    				string subExpr = getSubExpression(tokens, ref tokenIndex);
    				operandStack.Push(Eval(subExpr));
    				continue;
    			}
    			if (token == ")") {
    				throw new ArgumentException("Mis-matched parentheses in expression");
    			}
    			//If this is an operator  
    			if (Array.IndexOf(_operators, token) >= 0) {
    				while (operatorStack.Count > 0 && Array.IndexOf(_operators, token) < Array.IndexOf(_operators, operatorStack.Peek())) {
    					string op = operatorStack.Pop();
    					double arg2 = operandStack.Pop();
    					double arg1 = operandStack.Pop();
    					operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
    				}
    				operatorStack.Push(token);
    			} else {
    				operandStack.Push(double.Parse(token));
    			}
    			tokenIndex += 1;
    		}
    
    		while (operatorStack.Count > 0) {
    			string op = operatorStack.Pop();
    			double arg2 = operandStack.Pop();
    			double arg1 = operandStack.Pop();
    			operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
    		}
    		return operandStack.Pop();
    	}
    
    	private string getSubExpression(List<string> tokens, ref int index)
    	{
    		StringBuilder subExpr = new StringBuilder();
    		int parenlevels = 1;
    		index += 1;
    		while (index < tokens.Count && parenlevels > 0) {
    			string token = tokens[index];
    			if (tokens[index] == "(") {
    				parenlevels += 1;
    			}
    
    			if (tokens[index] == ")") {
    				parenlevels -= 1;
    			}
    
    			if (parenlevels > 0) {
    				subExpr.Append(token);
    			}
    
    			index += 1;
    		}
    
    		if ((parenlevels > 0)) {
    			throw new ArgumentException("Mis-matched parentheses in expression");
    		}
    		return subExpr.ToString();
    	}
    
    	private List<string> getTokens(string expression)
    	{
    		string operators = "()^*/+-";
    		List<string> tokens = new List<string>();
    		StringBuilder sb = new StringBuilder();
    
    		foreach (char c in expression.Replace(" ", string.Empty)) {
    			if (operators.IndexOf(c) >= 0) {
    				if ((sb.Length > 0)) {
    					tokens.Add(sb.ToString());
    					sb.Length = 0;
    				}
    				tokens.Add(c);
    			} else {
    				sb.Append(c);
    			}
    		}
    
    		if ((sb.Length > 0)) {
    			tokens.Add(sb.ToString());
    		}
    		return tokens;
    	}
    }

    Call the class and method like this:

    string formula = "type your formula here"; //or get it from DB
    StringToFormula stf = new StringToFormula();
    double result = stf.Eval(formula);

    Hope iot helps.

    Mitja

    • Marked as answer by Prathap Kumar Wednesday, March 07, 2012 11:41 AM
    Wednesday, March 07, 2012 11:18 AM
  • You can use the DataColumn.Expression Property. It evaluates any expression like the javascript eval function.

    So you can use it like this:

        class Program
        {
            static void Main(string[] args)
            {
                string formula = "(a+b)/c"; // Take it from the data base or whatever...
                Console.WriteLine("Enter first number:");
                string a = Console.ReadLine();
                Console.WriteLine("Enter second number:");
                string b = Console.ReadLine();
                Console.WriteLine("Enter third number:");
                string c = Console.ReadLine();
    
                // Replace all letters with numbers
                formula = formula.Replace("a", a).Replace("b", b).Replace("c", c);
    
                // Evaluate the formula
                Console.WriteLine("Formula: {0}\tResult: {1}", formula, Evaluate(formula));
                Console.ReadLine();
            }
    
            public static double Evaluate(string expression)
            {
                DataTable table = new DataTable();
                table.Columns.Add("myExpression", string.Empty.GetType(), expression);
                DataRow row = table.NewRow();
                table.Rows.Add(row);
                return double.Parse((string)row["myExpression"]);
            } 
    
        }

    Noam B.



    Do not Forget to Vote as Answer/Helpful, please. It encourages us to help you...

    • Proposed as answer by Noam B Wednesday, March 07, 2012 10:51 AM
    • Marked as answer by Prathap Kumar Wednesday, March 07, 2012 11:19 AM
    Wednesday, March 07, 2012 10:50 AM

All replies

  • I'm interested in this solution too.  MSFT has long needed the ability to interpret strings as commands and or variables.  The problem you will have is that there is no "Interpret" function in .NET framework that allows you to build a string that gets interpreted at run time.  So the second best thing is that you will have to build a parser to parse and intrepret each piece of the equation.  You are building a mini-compiler.  Some people will point you to the CODEDOM class in .NET... The idea is that you are going to build a class of code and then run a compiler and run to code all at run time.

    Others may point you to Generics where tricks are performed to get types back that you want. 

    There is another set of class you can use known as Expression Trees.  It takes a while to learn but I'm confident you can build a lambda expression which is a function that returns a result all done via strings in the Expression tree.


    JP Cowboy Coders Unite!

    Wednesday, March 07, 2012 4:13 AM
  • You can use the DataColumn.Expression Property. It evaluates any expression like the javascript eval function.

    So you can use it like this:

        class Program
        {
            static void Main(string[] args)
            {
                string formula = "(a+b)/c"; // Take it from the data base or whatever...
                Console.WriteLine("Enter first number:");
                string a = Console.ReadLine();
                Console.WriteLine("Enter second number:");
                string b = Console.ReadLine();
                Console.WriteLine("Enter third number:");
                string c = Console.ReadLine();
    
                // Replace all letters with numbers
                formula = formula.Replace("a", a).Replace("b", b).Replace("c", c);
    
                // Evaluate the formula
                Console.WriteLine("Formula: {0}\tResult: {1}", formula, Evaluate(formula));
                Console.ReadLine();
            }
    
            public static double Evaluate(string expression)
            {
                DataTable table = new DataTable();
                table.Columns.Add("myExpression", string.Empty.GetType(), expression);
                DataRow row = table.NewRow();
                table.Rows.Add(row);
                return double.Parse((string)row["myExpression"]);
            } 
    
        }

    Noam B.



    Do not Forget to Vote as Answer/Helpful, please. It encourages us to help you...

    • Proposed as answer by Noam B Wednesday, March 07, 2012 10:51 AM
    • Marked as answer by Prathap Kumar Wednesday, March 07, 2012 11:19 AM
    Wednesday, March 07, 2012 10:50 AM
  • Noam, just one thing: what will happen if this is not the only formula? If there  is like: "a*b / (c+d)" or what ever?

    You have to create a dinamic code to seperated formula into pieces.


    Mitja

    Wednesday, March 07, 2012 11:10 AM
  • This is how it should be done:

    public class StringToFormula
    {
    	private string[] _operators = { "-", "+", "/", "*","^"};
    	private  Func<double, double, double>[] _operations = {
    		(a1, a2) => a1 - a2,
    		(a1, a2) => a1 + a2,
    		(a1, a2) => a1 / a2,
    		(a1, a2) => a1 * a2,
    		(a1, a2) => Math.Pow(a1, a2)
    	};
    
    	public double Eval(string expression)
    	{
    		List<string> tokens = getTokens(expression);
    		Stack<double> operandStack = new Stack<double>();
    		Stack<string> operatorStack = new Stack<string>();
    		int tokenIndex = 0;
    
    		while (tokenIndex < tokens.Count) {
    			string token = tokens[tokenIndex];
    			if (token == "(") {
    				string subExpr = getSubExpression(tokens, ref tokenIndex);
    				operandStack.Push(Eval(subExpr));
    				continue;
    			}
    			if (token == ")") {
    				throw new ArgumentException("Mis-matched parentheses in expression");
    			}
    			//If this is an operator  
    			if (Array.IndexOf(_operators, token) >= 0) {
    				while (operatorStack.Count > 0 && Array.IndexOf(_operators, token) < Array.IndexOf(_operators, operatorStack.Peek())) {
    					string op = operatorStack.Pop();
    					double arg2 = operandStack.Pop();
    					double arg1 = operandStack.Pop();
    					operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
    				}
    				operatorStack.Push(token);
    			} else {
    				operandStack.Push(double.Parse(token));
    			}
    			tokenIndex += 1;
    		}
    
    		while (operatorStack.Count > 0) {
    			string op = operatorStack.Pop();
    			double arg2 = operandStack.Pop();
    			double arg1 = operandStack.Pop();
    			operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
    		}
    		return operandStack.Pop();
    	}
    
    	private string getSubExpression(List<string> tokens, ref int index)
    	{
    		StringBuilder subExpr = new StringBuilder();
    		int parenlevels = 1;
    		index += 1;
    		while (index < tokens.Count && parenlevels > 0) {
    			string token = tokens[index];
    			if (tokens[index] == "(") {
    				parenlevels += 1;
    			}
    
    			if (tokens[index] == ")") {
    				parenlevels -= 1;
    			}
    
    			if (parenlevels > 0) {
    				subExpr.Append(token);
    			}
    
    			index += 1;
    		}
    
    		if ((parenlevels > 0)) {
    			throw new ArgumentException("Mis-matched parentheses in expression");
    		}
    		return subExpr.ToString();
    	}
    
    	private List<string> getTokens(string expression)
    	{
    		string operators = "()^*/+-";
    		List<string> tokens = new List<string>();
    		StringBuilder sb = new StringBuilder();
    
    		foreach (char c in expression.Replace(" ", string.Empty)) {
    			if (operators.IndexOf(c) >= 0) {
    				if ((sb.Length > 0)) {
    					tokens.Add(sb.ToString());
    					sb.Length = 0;
    				}
    				tokens.Add(c);
    			} else {
    				sb.Append(c);
    			}
    		}
    
    		if ((sb.Length > 0)) {
    			tokens.Add(sb.ToString());
    		}
    		return tokens;
    	}
    }

    Call the class and method like this:

    string formula = "type your formula here"; //or get it from DB
    StringToFormula stf = new StringToFormula();
    double result = stf.Eval(formula);

    Hope iot helps.

    Mitja

    • Marked as answer by Prathap Kumar Wednesday, March 07, 2012 11:41 AM
    Wednesday, March 07, 2012 11:18 AM
  • Mitja;

       Congratulations that's some pretty code there.  Too bad it takes so much work to do this simple task.  The op may be able to create expression trees as well, the only problem as your code points out is that there has to be massive support for parsing.  As mentioned before, he is indeed having to write a mini-compiler/parser.  But I think you're code is an excellent example of how to do this.


    JP Cowboy Coders Unite!

    Wednesday, March 07, 2012 1:31 PM
  • thx :)

    I like coding, so ... hehe


    Mitja

    Wednesday, March 07, 2012 3:04 PM
  • Best and short answer i have seen as solution, let me dig deep into it, but if i directly say double.parse("string") is doesnt calculate the formula ? so there is something in the datatable

    Y2KPRABU, MCTS,MCPD INDIA

    Saturday, October 13, 2012 7:51 PM
  • Great source, thx.  

    On my C# compile, it complains about tokens.Add(c).  I have to convert the character to string, such as, tokens.Add(c.ToString()); 

    Tuesday, August 20, 2013 4:15 PM
  • I realize this is really old :-).

    I actually implemented it and I want to say firstly thank you very very much :-) for the great effort here.

    I am currently using it, but working on a bug that I found, in the order of operations.

    Again, amazing work and when I resolve the bug I will repost the change

    Issue:

    (1*2)+(3+7)-(12*2)-3

    The answer should be -15, however it actually comes up with -9. This is just one situation, but it can easily be reproduced.

    Operation Order should be

    (2) + (10) -(24)-3

    12-24-3

    -12-3

    -15

    Not -9, instead it's literally subtracting 3 from 12 versus adding -3 to -12.

    even changing it to

    (1*2)+(3+7)-(12-2)-3

    should give -1, but instead gives 5.

    2+10-10-3

    12-10-3

    2-3

    -1 (not 2+5 =5)

    Now if you wrap everything in ( ) as such

    ((1*2)+(3+7)-(12-2))-3, it actually comes up with the correct number, but you shouldn't have to do that to get -1.

    Anyhoo, I am currently trying to figure out how to resolve it. Unless someone can convince me otherwise :-), and that somehow the order of operations is incorrect based on my understanding. BTW you can also go here http://www.freemathhelp.com/order-of-operations.html and type in the formula and you get -1 whether you wrap it or not (and you get -15) in the first example regardless of wrapping.

    Again thanks for the huge effort here. If I can resolve the issue I will report.

    BTW, it doesn't always happen, it depends on factors (and I don't know them all), but so far I only see it as a problem with the end number, not with starting numbers etc.

    Thanks!!!

    Sunday, March 09, 2014 5:06 AM
  • Yes, this thread is old.  It is a great example of why you should not just tag onto the end of old threads.  The suggested solution is out of date.

    Use nuget to add the Roslyn package. http://www.nuget.org/packages/Roslyn

    Then the code is

    var engine = new Roslyn.Scripting.CSharp.ScriptEngine();
    var session = engine.CreateSession();
    int result = session.Execute<int>("(1*2)+(3+7)-(12*2)-3");
    

    Which is a good deal simpler than the currently marked answer.


    Paul Linton

    Sunday, March 09, 2014 6:06 AM
  • Thanks Paul,

    I do appreciate the response.

    Just to be clear though, that's terrible advice to say you shouldn't look or use old threads, if that were true they should be deleted at some "insert time" and they aren't on purpose.

    This article is also from 2012, which is the last (CTP) from Microsoft.

    Not something I plan on using in my production applications since it hasn't moved forward in 2 years.

    Easier, but not a model I want to use for Production basing my life on year old not updated technology.

    Cheers


    Sunday, March 09, 2014 2:20 PM
  • Again I wanna thank Mitja for the great work.

    Since you can't use (nor would I), Roslyn in production (as it's a CTP and the CTP license actually the term was up in January (2014)), I took a few minutes to debug the code from Mitja.

    Here is what I found (which answers why there was a problem.

    Since you are using STACK and you are pushing the Eval values to the TOP of the Operand / Operate stacks.

    The problem is that the code is actually evaluating the answers (subexpressions) from Right to left not left to right.

    So instead of it (in my example)

    (1*2)-(2*2)-5, which would be (2)-(4)-5, or (2-4) - 5, -2-5 or -7, it actually comes up with -3.

    It does this because in the STACKS you have

    5,4,2 and then -,- respectively

    So it's doing 4-5 (-1), then doing 2- (-1) or 3.

    So relative to the subexpressions (depending on how complex they are), it works, but as soon as it starts processing the larger set of combined (total list) of values it is incorrect.

    I'm re-working it now and will come back with the resolution.

    Again this is all based on the great work from Mitja.

    Thanks!

     

    Sunday, March 09, 2014 5:44 PM
  • Entirely your choice.

    Use buggy code you found on some random site on the internet or the product that Microsoft uses to compile Visual Studio and is due to become the compiler that we will use all day every day in the near future.


    Paul Linton

    Sunday, March 09, 2014 8:36 PM
  • Actually you are giving terrible and uneducated advice

    1. The last release was a CTP

    2. The CTP is not only NOT production ready, but the license itself

    a) expired in January 2014

    b) is not allowed to be used in production (unless of course I decide to do TAP which they aren't inviting anyone too right now).

    So, even if I wanted to use it, it is against the EULA (which is expired), per the NuGet Download (Microsoft license for Roslyn), is against the rules.

    Again, it would be impossible to use this in production right now (it's illegal per the expired EULA Terms).

    That being said, I was fortunately to find something else which works extremely well, several things.

    Sooo, yeah I'll use something else, versus the suggested, which MS says not to use in production...

    Sunday, March 09, 2014 10:46 PM
  • Paul;

     Nice example, and long overdue from MSFT.  What you show is indeed something that's been available in other languages for over 15 years now.  For example the REXX language can do this and has been doing it since at least 1987. 

     Regardless of Roysalyn's readiness for production it's about time there is finally an upcoming supported way of having strings interrupted as code.  MSFT has been inexcusably late to the party.


    JP Cowboy Coders Unite!


    Monday, March 10, 2014 1:37 AM
  • "Entirely your choice."
    "Actually you are giving terrible and uneducated advice"

    A little harsh, don't you think?  Mentioning that people can make their own choice being described as "uneducated" seems a trifle over the top to me.


    Paul Linton

    Monday, March 10, 2014 1:38 AM
  • Anokneemous;

      I always find it amazing how people that come here for help like to strap on self-righteous attitude and scold those who showed an elegant way, and then go on to say I found solutions to this but don't share them.

      Why did you ever come here in the first place?


    JP Cowboy Coders Unite!

    Monday, March 10, 2014 1:39 AM
  • I find it amazing, that people can be self righteous and passive aggressive and expect that others just suck it up.

    A CTP that you cannot legally use in production, is not a solution. Not only that, but the way the answers are written, first take a pot shot at me, then "answer".

    I on the other hand was trying to be supportive to the original post of the original poster as honestly it seemed viable.

    I didn't say "Oh your solution would have been good if it always worked", "but let me show you how it should be". That's the type of response Paul is giving.

    Yes, this thread is old.  It is a great example of why you should not just tag onto the end of old threads.  The suggested solution is out of date.

    The initial part of Paul's answer has nothing to do with anything other than putting in a dig for no reason and adds absolutely zero value to the answer. If you can dish it out, learn to take it back.

    As I said, thank you for the suggested solution, but it doesn't work as a solution.

    I also didn't post the solution I saw yet, because I was verifying that it actually does what it says, as I am using this for Windows Phone 8.x and the Surface 8.x and unfortunately Roslyn doesn't work for the phone either.

    The secondary solution I found unfortunately didn't work with Phone due to the library requirements.

    The final one I found later is actually an example that I had to modify to make it work for my Phone App but works great.

    It's based off of http://nicoschertler.wordpress.com/2011/09/22/math-parser-using-lambda-expressions/

    And yes, if I want to use buggy code, versus the illegal use of an invalid license yes. Just because I don't accept the answer as a viable or usable answer, as I said thanks, doesn't mean I don't appreciate it, but there is no need to make sarcastic comments / response merely because of that.

    Anyhoo, I wasted enough time on this. Have a good one.

    Monday, March 10, 2014 3:05 AM
  • To me, 5 years from now when people read this thread they will see Paul's answer and BTW Rosalyn will already have been in production for 4 years.  What we're seeing here is a new technology filling a HUGE gap MSFT has always had.  I know from experience that what Paul posted will be the solution of the future. 

    We also know that CTPs like Asynch framework, EF, MEF, and Reactive Extensions were put out years in advance on purpose.  MSFT want's us to report bugs and help with improvement suggestions.  If we prototype these frameworks in a pseudo-production environment they are happy to get the feedback and we are happy to use the product. 

    As far as I'm concerned the Roslyn solution is and always will be the best solution for this issue.


    JP Cowboy Coders Unite!

    Monday, March 10, 2014 2:02 PM
  • Thanks Mr. Javaman,

    Someday it maybe true, and in fact, it is indeed a good technical solution respectively.

    However, the facts still speak for themselves, which are not in our favor.

    1. It is a CTP, which is used to have people test it and provide bug / usability feedback / requests. Whether I use something else or not, the CTP can be just as buggy as any other code, regardless that Microsoft wrote it, and they know it.

    2. The CTP itself is not intended for or supported in a production environment. I need this in production, so it is not a solution at all. Therefor it cannot be the best solution (for me), or anyone else who needs it for production.

    3. It does not work on Windows Phone or more specifically .NET Compact Framework, which is what XBOX, Phone and other devices are built on. Therefor, as I need it for Windows Phone (as mentioned), it in fact again, is not a solution I can use. Therefor it cannot be the best solution (for me), or anyone who needs it for ref.emit / compact frameworks.

    4. One actual benefit is that in fact, while the EULA online shows it expired in 2014, there is an extension so Rosyln CTP is good until 2015 (January).

    I am grateful for the suggestion, unfortunately no matter how many times :-) we go back and forth it is not a production viable or supported solution.

    Someday maybe, but until Microsoft puts out official words, then even your other statements are merely hypothetical in nature.

    Microsoft themselves have stated not to use it in production and that it is only for test. I certainly never said you shouldn't use the CTP for that. Merely that Paul's comments about my choice in using buggy code versus Microsoft's stuff is honestly ridiculous at best. As their code is buggy (check the forums), and to your point, they know it, so they want us to test it. NOT put it in production.

    So if you can explain to me how a framework that doesn't work on .NET Compact Framework, nor is it supported or intended for Production, is somehow the best solution, then please enlighten the world.

    Otherwise you should stop trying to defend your friend and accept defeat... graciously of course.

    Cheers and see you in 5 years.

    Monday, March 10, 2014 4:02 PM
  • Legality aside, I was using Async CTP as well as EF CTP in production for years (collectively) and yes there are bugs in these products; however, the tests run in the applications didn't reveal any for the narrow scope I used them.  So while your warnings are technically true, there more fodder for a lawyer than someone trying to solve nasty little MSFT problems. 

    But let's assume Prathap uses the "Marked Answer" as his solution, he's happy I guess and that's the end of it, as for me and my house "we will follow Rosalyn" ready or not.


    JP Cowboy Coders Unite!

    Monday, March 10, 2014 7:43 PM
  • Thanks for clearing up that your question has very little to do with the OP.  I don't see any mention of Production or Windows Phone in the OP.  If you have a problem start a thread, it is very cheap.  Don't hijack another thread, tell us that the answer given doesn't work, abuse the people who make the answer work, add new provisions and then produce the earth shattering statement that 'code is buggy'.  Wow!  Really?  Did you work that out by yourself?

    Paul Linton

    Monday, March 10, 2014 9:40 PM