Reputation: 756
I have a list of document numbers to be evaluated. This list is very large and I would rather not cast all document numbers to an int because they can also contain letters. Here is a sample list of numbers.
1070
1071
1072
1073
1074
1075
1076
1077
1078
CO1089
CO1099
CO2000
These numbers are contained in objects in C# and I run Linq to return a list of objects in a Range. Here is my linq code.
results = from row in MyObjectList.AsQueryable<MyObject>()
where String.Compare(row.Header.DocNumber, _sDocumentStartNumber) >= 0
&& String.Compare(row.Header.DocNumber, _sDocumentEndNumber) <= 0
select row;
If I have a range of
From: 1
To: 10000
A user would expect to get back all of the numeric numbered transactions within that range. However, since i am comparing strings I get back no results.
I could detect that my to and from values are numeric and then evaluate only transactions with numbers and use a linq statement based on that (or edit the linq statement to do the conversion and comparisons), but I am concerned with performance at this point since the lists can be rather large when returning.
I am open to suggestions here and know that there may be many paths that all lead to the same result. My key concern in this is performance. Remember that I could be performing this action on very large sets of data and I need the results returned in a reasonable amount of time.
Thank you in advance for any and all suggestions.
~~~ EDIT ~~~
The objects that I am leveraging is Intuit IPP SDK objects for QuickBooks Online. I am querying intuit for documents (such as invoices) and need to sort on the document numbers. Intuit does not do this on their server side so I have to do this on my side. Depending on what the user enters for other search criteria I could end up with ALL of their documents in a returned List.
Upvotes: 0
Views: 88
Reputation: 5766
Create your own comparer, say DocumentNumberWithinRangeComparer
:
public class DocumentNumberWithinRangeComparer
{
public int? RangeFrom { get; set; }
public int? RangeTo { get; set; }
public DocumentNumberWithinRangeComparer(int? from, int? to)
{
RangeFrom = from;
RangeTo = to;
}
public bool IncludeInResults(MyObject obj)
{
if (!RangeTo.HasValue || !RangeFrom.HasValue)
return true;
int docnumber;
if (!Int32.TryParse(obj.Header.DocNumber, out docnumber))
return false;
return docnumber >= RangeFrom.Value && docnumber <= RangeTo.Value;
}
}
Then create an instance of it and use that to filter:
var comparer = new DocumentNumberWithinRangeComparer(0,100);
var results = from row in MyObjectList.AsQueryable<MyObject>()
where comparer.IncludeInResults(row)
select row;
If you want faster comparing, you should pre-process your "DocNumber" to be integers, but this only makes sense if you process the list of documents once and then query it multiple times.
A simple solution for pre-processing would be to create a Dictionary<string, int>
from Header.DocNumber
string value and the parsed number value. For values that dont parse you could use something like -1
, Int32.MinValue
, or make a more advanced parser that can deal with non-numeric values.
Once you have the dictionary you could query like this...
var dictionary = new Dictionary<string, int>(); // Fill this...
var results = from row in MyObjectList.AsQueryable<MyObject>()
where dictionary[row.Header.DocNumber] >= _sDocumentStartNumber &&
dictionary[row.Header.DocNumber] <= _sDocumentEndNumber
select row;
It will take some extra time to set up this dictionary, but it will pay back if you query the same document set multiple times.
Upvotes: 1
Reputation: 134045
When you get the range, left-pad the starting number with spaces so that it's the same length as the ending number. So in your example, your ending number is "10000". So make your starting number "__1" (that is, 4 spaces and then a 1).
When comparing, left-pad the document number before comparing. So:
string paddedStart = _sDocumentStartNumber.PadLeft(_sDocumentEndNumber.Length);
string padded;
results = from row in MyObjectList.AsQueryable<MyObject>()
let padded = row.Header.DocNumber.PadLeft(paddedStart.Length)
where String.Compare(padded, paddedStart) >= 0
&& String.Compare(padded, _sDocumentEndNumber) <= 0
select row;
Upvotes: 0