Search code examples
c#performanceintegerstring-comparison

Most efficient way to compare a string integer against a range within C#


I have an integer range I need to check against, but the field I'm using for the comparison is a string.

This string could have non-integer values in it. Strings with non-integer values should be skipped.

The range is simply two other integers.

I want to know what the fastest way to do this is, if there are ten thousand numbers in the list of strings. I've considered simply int.TryParse, and then conditional checks between the range, but I want to know if there is a faster way to do it.

Not looking for opinions, keen to see benchmark numbers.

Below is an example of what the data and range min/max might look like.

int min = 1000;
int max = 4999;

List<string> orderNumbers = new List<string>
{
    "4021",
    "*1002",
    "5000",
    "4500"
};

Solution

  • TryParse would work well

    var results = orderNumbers
         .Select(x => int.TryParse(x, out var i) ? i : (int?) null)
         .Where(x => x >= min && x <= max)
         .Cast<int>() // resolve the nullable;
    

    Note : you could easily use .Select(x => x.Value) it will essential create the same exact IL


    If you are doing this often, then ideally your list wouldn't be of type string.

    Or just use a foreach loop

    var results = new List<int>(orderNumbers.Count);
    foreach (var item in orderNumbers)
       if (int.TryParse(item, out var value) && value >= min && value <= max)
          results.Add(value);
    

    If you are bored, create your own Iterator extension method

    public IEnumerable<int> GetRange(this IEnumerable<string> source, int min, int max)
    {
       foreach (var item in source)
          if (int.TryParse(item, out var value) && value >= min && value <= max)
             yield return value;
    }
    

    Usage

    var results = orderNumbers.GetRange(min,max);
    

    If you were looking at raw efficiency, and your valid numbers were always just numbers with no cultural differences like 1000's separators and such. You may get better performance out of your own TryParse method and (optionally) iterating chars with pointers or Span<T>

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static unsafe bool MyTryParse(string source, int min, int max, out int value)
    {
       value = 0;
       fixed (char* p = source)
       {
          for (var i = 0; i < source.Length; i++)
             if (p[i] >= '0' && p[i] <= '9') 
                value = value * 10 + p[i] - '0';
             else 
                return false;
       }
       return value >= min && value <= max;
    }