locked
How to update a string by LINQ when condition matched RRS feed

  • Question

  • i tried this code but did not compile

    char[] array1 = { '+', '-', '*','/','%' };
    string formula2 = "Consensus Model~Total Operating Expenses-GAAP~CL~4Q 2014 + Consensus Model~Total Operating Expenses-GAAP~CL~3Q 2014 - Consensus Model~Total Operating Expenses-GAAP~CL~2Q 2014 / Consensus Model~Total Operating Expenses-GAAP~CL~1Q 2014";
    
    foreach (var foundchar in formula2.Where(w => array1.Where(z=> z.ToString()==w.ToString()).ToList()))
    {
    	foundchar  = "#";
    }

    basically i want to search formula2 string variable and if any char in formula2 match with a char in char array then it will be replaced by #.

    but my code is not compiling. so all operator will be replaced by # for right output. thanks


    Monday, September 14, 2020 8:19 AM

Answers

  • formula2 is a string so that means Where is enumerating the characters (a value type). Each time through the loop the local variable `foundchar` is set to the current character. But since this is a value type the original value is unchanged. This would be the same behavior if you had tried it on an int.

    LINQ is overkill for this very common problem. Note that I don't agree that you should be replacing all those tokens with # as there is no way to roundtrip back so I really question your logic here but neverthelesss it is easy to do.

    //Inefficient but clean
    foreach (var delimiter in array1)
        formula2 = formula2.Replace(delimiter, '#');
    
    Console.WriteLine(formula2);

    It requires 1 enumeration per delimiter and the corresponding string allocate so it is better for small strings with limited delimiters. A more general purpose solution using StringBuilder is far more efficient but requires more code.

    string ReplaceAll ( string target, string replacement, params char[] delimiters ) 
    {
        var builder = new System.Text.StringBuilder();
        foreach (var ch in target)
        {
            if (delimiters.Contains(ch))
                builder.Append(replacement);
            else
                builder.Append(ch);
        };
    
        return builder.ToString();
    }
    
    var result = ReplaceAll(formula2, "#", array1);


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Sudip_inn Monday, September 14, 2020 3:32 PM
    Monday, September 14, 2020 2:47 PM

All replies

  • formula2 is a string so that means Where is enumerating the characters (a value type). Each time through the loop the local variable `foundchar` is set to the current character. But since this is a value type the original value is unchanged. This would be the same behavior if you had tried it on an int.

    LINQ is overkill for this very common problem. Note that I don't agree that you should be replacing all those tokens with # as there is no way to roundtrip back so I really question your logic here but neverthelesss it is easy to do.

    //Inefficient but clean
    foreach (var delimiter in array1)
        formula2 = formula2.Replace(delimiter, '#');
    
    Console.WriteLine(formula2);

    It requires 1 enumeration per delimiter and the corresponding string allocate so it is better for small strings with limited delimiters. A more general purpose solution using StringBuilder is far more efficient but requires more code.

    string ReplaceAll ( string target, string replacement, params char[] delimiters ) 
    {
        var builder = new System.Text.StringBuilder();
        foreach (var ch in target)
        {
            if (delimiters.Contains(ch))
                builder.Append(replacement);
            else
                builder.Append(ch);
        };
    
        return builder.ToString();
    }
    
    var result = ReplaceAll(formula2, "#", array1);


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Sudip_inn Monday, September 14, 2020 3:32 PM
    Monday, September 14, 2020 2:47 PM
  • Sir basically i am looking for way to use LINQ to update operation in formula2 variable with hash sign.

    my all operator stored in char array.

    how to do it by LINQ. please share the idea. thanks

    Monday, September 14, 2020 3:32 PM
  • You can't. LINQ is query, not modification. (Note: You can transform values and produce an end result but the original value remains unchanged and is transformed each time it is requested) To actually modify the string you use the standard string operations. You are entirely too fixated on LINQ. LINQ is designed for a very specific problem, not for general use. Overuse of LINQ results in poor performance and harder to read code. In fact a lot of code is starting to set up guidelines to avoid LINQ. Use the function I provided and you're good.

    Michael Taylor http://www.michaeltaylorp3.net


    • Edited by CoolDadTx Monday, September 14, 2020 4:06 PM EDIT: added note about transformation
    Monday, September 14, 2020 4:03 PM
  • I have done the job this way.

                string[] operators = { "+", "*", "/", "%" };
                string formula2 = "Consensus Model~Total Operating Expenses-GAAP~CL~4Q 2014 + Consensus Model~Total Operating Expenses-GAAP~CL~3Q 2014 + Consensus Model~Total Operating Expenses-GAAP~CL~2Q 2014 / Consensus Model~Total Operating Expenses-GAAP~CL~1Q 2014";
    
                foreach (var data in formula2.Where(w => operators.Contains(w.ToString())))
                {
                   if(formula2.Contains(data.ToString()))
                   {
                       formula2 = formula2.Replace(data.ToString(), "#");
                   }
                }

    thanks

    Tuesday, September 15, 2020 8:58 AM
  • That is really inefficient. You are enumerating each character in the string looking for one of your replacement characters. Once you have them you are searching again with the if. But since the Where would only return the characters that are in the string you are searching again even though it will always be true. Then you search the string a final time to actually do the replacement. So you're running through the string 3 times for each match and 1 of those times is wasteful.

    The approach I gave is faster and easier but do what you want. Don't be surprised when you profile this code and this block of code is identified as a performance issue. LINQ is not the correct solution for this (any many) problems. Don't use it where it is not needed.


    Michael Taylor http://www.michaeltaylorp3.net

    Tuesday, September 15, 2020 1:36 PM
  • Thank u sir i will follow your approach.
    Tuesday, September 15, 2020 4:27 PM