The other day I was presented with the very common problem of turning a comma-separated list of numbers into an array.
Now the classic way of doing this (given a string numbersCSV
) is:
string[] splitString = numbersCSV.Split(',');
int?[] intResult = new int?[splitString.Length];
for (int j = 0; j < splitString.Length; j++)
{
int r;
if (int.TryParse(splitString[j], out r))
{
intResult[j] = r;
}
else
{
intResult[j] = null;
}
}
... Or if you want to be more terse about it:
string[] splitString = numbersCSV.Split(',');
int?[] intResult = new int?[splitString.Length];
for (int j = 0; j < splitString.Length; j++)
{
int r;
intResult[j] = int.TryParse(splitString[j].Trim(), out r) ? (int?)r : null;
}
I wondered if there was an even terser (and sexier) way using predicates and LINQ extension methods. I came up with three variations (that return List<int?>
), all bristling with one-line LINQ-extension / predicate goodness:
IEnumerable<int?> intList2 = numbersCSV.Split(',').Select<string, int?>(
s => { int i; return int.TryParse(s.Trim(), out i) ? i : (int?)null; });
This splits the string and projects the results (using the Select<>
extension) into a list of Nullable<int>
s using a Func<>
lambda.
List<int?> intList3 = new List<int?>(20);
numbersCSV.Split(',').ToList().ForEach(s => { int i; if (int.TryParse(s.Trim(), out i)) { intList3.Add(i); } else { intList3.Add(null); } });
This one splits the string and calls ForEach<>
extension with an Action<>
lambda.
IEnumerable<int?> intList1 = numbersCSV.Split(',').ToList(). ConvertAll<int?>(s => { int i; if (int.TryParse(s.Trim(), out i)) { return i; } else { return null; } });
This one splits the string and calls the Convert<>
extension with a Converter<>
lambda.
Clear as mud.
Of course, the natural question (other than "Huh?") is how they perform relative to the straightforward way? Without getting into the gory details*, I found that the first method is the best, with a 15% worse execution time as compared to the "classic" way.
Is the savings in vertical space worth the performance hit and illegible code?
... Well, probably not, but I it was a fun learning experience =)
*The gory details: I chose nullable ints because I wanted a little robustness (no exceptions thrown). I constructed random comma-separated lists and did multiple trials of 500,000 loops of the parse routine. To force the enumeration of the ints, I took the sum of the non-null values and printed them out to the console.