Using LINQ, how do you filter a list of strings for values ​​that match the pattern "q1, q2", etc.?

It should be simple, but I'm looking for a better answer, at least in terms of performance and elegance.

I have a list of strings, and some of the values ​​are q1, q2, q3, etc.

I would like to choose them.

What would be the best way to do this?

0


a source to share


3 answers


It looks like you are trying to do micro-optimizations, which means that you will spend a lot of effort to keep your program running at the same speed. Focus on code clarity first, then optimize what's actually slower.

Assuming you've profiled and found this is the bottleneck of your application in real-world scenarios:

Inelegant (and often a performance drag) uses exceptions for non-exceptional cases . See http://www.developerfusion.com/code/4650/validating-an-integer/ for example.

Depending on the limitations of your situation, you're probably better off changing yours IsInteger()

to do one of the following:

bool int.TryParse(string s, out int result);

      

(see http://msdn.microsoft.com/en-us/library/system.int32.tryparse.aspx )

or



Microsoft.VisualBasic.Information.IsNumeric(object expression)

      

(see http://www.hanselman.com/blog/ExploringIsNumericForC.aspx )

or



x >= '0' && x < '9'

      

And then:

.Where(c => c[0] == 'q' && IsInteger(c[1]))

      

+1


a source


The best answer is to use either Regex or Jay Bazuzi's int.TryParse suggestion .

For a quick example, try this in LINQPad :

void Main()
{
    int n = 100;

    string[] a = {"q2", "q3", "b"};
    a = a.Concat(Enumerable.Repeat(0,n).Select(i => "qasd")).ToArray(); /* Random data */

    /* Regex Method */
    System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex("^q[0-9]+$");
    List<string> regMethod = a.Where(c => r.IsMatch(c)).ToList().Dump("Result");

    /* IsInteger Method */
    List<string> intMethod = a.Where(c => c.StartsWith("q") && IsInteger(c.Substring(1))).ToList().Dump("Result");

    /* int.TryParse Method suggest by Jay Bazuzi */
    int e = 0;
    List<string> parseMethod = a.Where(c => c.StartsWith("q") && int.TryParse(c.Substring(1), out e)).ToList().Dump("Result");
}

public static bool IsInteger(string theValue)
{
   try
   {
       Convert.ToInt32(theValue);
       return true;
   } 
   catch 
   {
       return false;
   }
}

      

Try commenting out one of the two methods at a time and try the performance for different values ​​of n.

My experience (on my Core 2 Duo laptop) seems to suggest:

n = 100. Regex takes about 0.003 seconds, IsInteger takes about 0.01 seconds
n = 1,000. Regex takes about 0.004 seconds, IsInteger takes about 0.07 seconds
n = 10,000. Regex takes about 0.007 seconds, IsInteger takes about 0.67 seconds
n = 100,000. Regex takes about 0.046 seconds, IsInteger takes about 6.9 seconds
n = 1,000,000. Regex takes about 0.42 seconds, IsInteger takes about 1 minute and 6 seconds

      

ParseMethod has the same performance as Regex (slightly faster if done both in the code example and in a separate method, that is, it replaces the body of the IsInteger method).



Note. The cost of creating lines is not included (enter a diff date if you like), but the cost is the same for both methods.

These numbers are much closer if most keys don't have "q" (IsInteger is never called), but Regex is just as good or better even if it is.

those. (for the "asdasd" placeholder string, not "qasd"):

n = 100. Regex takes about 0.003 seconds, IsInteger takes about 0.003 seconds
n = 1,000. Regex takes about 0.004 seconds, IsInteger takes about 0.004 seconds
n = 10,000. Regex takes about 0.005 seconds, IsInteger takes about 0.005 seconds
n = 100,000. Regex takes about 0.023 seconds, IsInteger takes about 0.025 seconds
n = 1,000,000. Regex takes about 0.21 seconds, IsInteger takes about 0.22 seconds

      

Again ParseMethod has the same performance as Regex.

Conclusion: Use either Regex or TryParse, it will be much faster in the worst case and otherwise

However, are there better / faster ways to select int values ​​from a set of strings? Perhaps a generic filter that compiles faster somehow?

+1


a source


The bottleneck you have with IsInteger is probably due to trying.

I tried replacing IsInteger with TryParse, and I got the following results (with n = 1,000,000):

Regex method: 540ms

TryParse method: 537ms

I used the following code for the second method:

Func<string, bool> lambda = (string c) => { Int32 temp; 
                                    return c.StartsWith("q") 
                                    && int.TryParse(c.Substring(1),out temp); };
List<string> intMethod = a.Where(lambda).ToList();

      

Moral of the story ...

While I usually prefer to use Regex, in this simple case where string manipulations are straightforward, TryParse's solution is fine. And for performance, it doesn't really matter which method you use, but don't use exception handling to check if some string value is int!

+1


a source







All Articles