0

I am trying to replace a variable with its value in a text string, which is a mathematical expression.

math_Expresion = d + dft-t 

to its current values :

 1 + 32 - 4

and then calculate it with a DataTable ()

var result = new DataTable().Compute(math_Expresion, null);

But it doesn't work if a variable as name contains another, example

a = 1
aa = 22
d = a + aa
d.Replace ("a", a)
d.Replace ("aa", aa)
return 1 + 11 not 1 + 22

is confusing the names. Is there a way to replace the names with their values without skipping them, some ReplaceExacly() function, or something similar to avoid this discomfort?

Actual code:

    for (int i = 0; i < variables_cache.Count; i++)
    {
        if (math_expresion.Contains(variables_cache[i].name))
        {
            math_expresion = math_expresion.Replace(variables_cache[i].name, variables_cache[i].value);
        }
    }

    var result = new DataTable().Compute(math_expresion, null);
Moix
  • 35
  • 6
  • 1
    switch `d.Replace ("a", a)` & `d.Replace ("aa", aa)` – CorrieJanse Dec 05 '21 at 20:24
  • This is done in a for loop, and I have no control over the order of the elements, so... – Moix Dec 05 '21 at 20:29
  • Sounds like you are reinventing the wheel of [dynamic compilation](https://stackoverflow.com/questions/826398/is-it-possible-to-dynamically-compile-and-execute-c-sharp-code-fragments) – Charlieface Dec 05 '21 at 20:37
  • 1
    @Moix you're the one writing the code, aren't you? then who else but you has control? you could sort your values descending by length. – Franz Gleichmann Dec 05 '21 at 20:38

1 Answers1

1

You don't have to do anything with formula, you can pass variables as DataColumns:

private static object Compute(string formula, 
                              IDictionary<string, object> variables = null) {
  // using - don't forget to Dispose table which is IDisposable
  using (DataTable table = new DataTable()) {
    // If we have variables... 
    if (variables != null)
      foreach (var pair in variables) // ... we create columns 
        table.Columns.Add(pair.Key, pair.Value.GetType()).DefaultValue = pair.Value;

    // last column is computation result
    table.Columns.Add().Expression = formula;

    // result is the value of the last (computed) column
    return table.Rows.Add()[table.Columns.Count - 1];
  }
} 

Usage:

var variables = new Dictionary<string, object> {
  { "d", 1},
  { "dft", 32},
  { "t", 4},
};

var result = Compute("d + dft-t", variables);

Console.Write(result);

Outcome:

29

If you insist on string processing you can try regular expressions to replace smartly:

  using System.Text.RegularExpressions;

  ...

  var variables = new Dictionary<string, object> {
    { "d", 1},
    { "dft", 32},
    { "t", 4},
  };

  var formula = "d + dft-t";

  formula = Regex.Replace(
      formula,
    @"\b\p{L}[\p{L}\d_]*\b",
      m => variables.TryGetValue(m.Value, out var value) 
           ? value?.ToString() 
           : m.Value);

  Console.Write(formula);

Outcome:

1 + 32-4

Pattern Explained:

\b          - word border
\p{L}       - letter (unicode one, we can use, say, cyrillic letters as well)
[\p{L}\d_]* - zero or more letters, digits, or _
\b          - word border

Edit: To get dictionary from variables_cache you can use Linq:

IDictionary<string, object> dict = variables_cache
  .ToDictionary(item => item.name, (object) (item.value));

tehcnically, you don't need dictionary and can easily use you own structure:

// Assuming that MyVariable has Name and Value properties or fields
private static object Compute(string formula, 
                              IEnumerable<MyVariable> variables = null) {
  // using - don't forget to Dispose table which is IDisposable
  using (DataTable table = new DataTable()) {
    // If we have variables... 
    if (variables != null)
      foreach (var pair in variables) // ... we create columns 
        table.Columns.Add(pair.Name, pair.Value.GetType()).DefaultValue = pair.Value;

    // last column is computation result
    table.Columns.Add().Expression = formula;

    // result is the value of the last (computed) column
    return table.Rows.Add()[table.Columns.Count - 1];
  }
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • I use a list of the variable class, with two parameters: name and value; some way to integrate it without distionary?, [more detail in my question, I have edited it] – Moix Dec 06 '21 at 09:24
  • [Convert List to Dictionary](https://stackoverflow.com/questions/11581101/c-sharp-convert-liststring-to-dictionarystring-string) seems not that complicated... – Luuk Dec 06 '21 at 09:34
  • @Moix: yes, for sure; please, see my edit. In the first code sample, you actually don't need a dictionary (I've provided it as an example how to store the variables) – Dmitry Bychenko Dec 06 '21 at 09:34