I think the best way to explain my question is with a short (generic) linq-to-objects code sample:
IEnumerable<string> ReadLines(string filename)
{
string line;
using (var rdr = new StreamReader(filename))
while ( (line = rdr.ReadLine()) != null)
yield return line;
}
IEnumerable<int> XValuesFromFile(string filename)
{
return ReadLines(filename)
.Select(l => l.Substring(3,3))
.Where(l => int.TryParse(l))
.Select(i => int.Parse(i));
}
Notice that this code parses the integer twice. I know I'm missing an obvious simple way to eliminate one of those calls safely (namely because I've done it before). I just can't find it right now. How can I do this?
How about:
int? TryParse(string s)
{
int i;
return int.TryParse(s, out i) ? (int?)i : (int?)null;
}
IEnumerable<int> XValuesFromFile(string filename)
{
return from line in ReadLines(filename)
let start = line.Substring(3,3)
let parsed = TryParse(start)
where parsed != null
select parsed.GetValueOrDefault();
}
You could probably combine the second/third lines if you like:
return from line in ReadLines(filename)
let parsed = TryParse(line.Substring(3,3))
The choice of GetValueOrDefault
is because this skips the validation check that casting (int)
or .Value
perform - i.e. it is (ever-so-slightly) faster (and we've already checked that it isn't null
).
It's not exactly pretty, but you can do:
return ReadLines(filename)
.Select(l =>
{
string tmp = l.Substring(3, 3);
int result;
bool success = int.TryParse(tmp, out result);
return new
{
Success = success,
Value = result
};
})
.Where(i => i.Success)
.Select(i => i.Value);
Granted, this is mostly just pushing the work into the lambda, but it does provide the correct answers, with a single parse (but extra memory allocations).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With