Reputation: 22064
I have an array string[]
with values that are mostly convertible to integers.
var values = new [] {"", "1", "2", "a", "3"};
I need to convert values to an array of integers, discarding any items that aren't convertible. So I end up with
var numbers = new [] {1, 2, 3};
What would be the most efficient (quickest and clean code) way to do this?
Upvotes: 1
Views: 599
Reputation: 6993
var numbers = values
.Where(x => !String.IsNullOrEmpty(x))
.Where(x => x.All(Char.IsDigit))
.Select(x => Convert.ToInt32(x))
.ToArray();
Upvotes: 0
Reputation: 13299
EDIT: Updated to not use try/catch, since StackOverflow users have pointed out that it's slow.
Try this.
var values = new[] { "", "1", "2", "a", "3" };
List<int> numeric_list = new List();
int num_try = 0;
foreach (string string_value in values)
{
if (Int32.TryParse(string_value, out num_try) {
numeric_list.Add(num_try);
}
/* BAD PRACTICE (as noted by other StackOverflow users)
try
{
numeric_list.Add(Convert.ToInt32(string_value));
}
catch (Exception)
{
// Do nothing, since we want to skip.
}
*/
}
return numeric_list.ToArray();
Upvotes: 2
Reputation: 45475
Here is my take on this. It uses a separate method but cleanly expresses the conditional parse without using nullable values:
private static IEnumerable<int> ParseInt32s(IEnumerable<string> value)
{
foreach(var value in values)
{
int n;
if(Int32.TryParse(value, out n))
{
yield return n;
}
}
}
Usage:
string[] values;
var parsedValues = ParseInt32s(values).ToArray();
Upvotes: 4
Reputation: 78292
var numbers = values.Select(
s => {
int n;
if (!int.TryParse((s ?? string.Empty), out n))
{
return (int?)null;
}
return (int?)n;
}
)
.Where(n => n != null)
.Select(n => n.Value)
.ToArray();
Upvotes: 4
Reputation: 34187
This can be done in a single linq statement without having to do the parse twice:
var numbers = values
.Select(c => { int i; return int.TryParse(c, out i) ? i : (int?)null; })
.Where(c => c.HasValue)
.Select(c => c.Value)
.ToArray();
Upvotes: 3
Reputation: 128427
I personally use an extension method that's a little different from what others have posted so far. It specifically uses a custom WeakConverter<TSource, TResult>
delegate to avoid the issue in Ron's answer of calling ToString()
on every object, while maintaining genericity unlike Jared's answer (though I will concede that sometimes trying to make everything generic is overdoing it -- but in this case, the added effort is really not much for what I consider a significant benefit in terms of reusability).
public delegate bool WeakConverter<TSource, TResult>(TSource source, out TResult result);
public static IEnumerable<TResult> TryConvertAll<TSource, TResult>(this IEnumerable<TSource> source, WeakConverter<TSource, TResult> converter)
{
foreach (TSource original in source)
{
TResult converted;
if (converter(original, out converted))
{
yield return converted;
}
}
}
With this in place, you can convert a string[]
to an int[]
quite simply and robustly (no double-parsing necessary):
string[] strings = new[] { "1", "2", "abc", "3", "", "123" };
int[] ints = strings.TryConvertAll<string, int>(int.TryParse).ToArray();
foreach (int x in ints)
{
Console.WriteLine(x);
}
Output:
1 2 3 123
Upvotes: 3
Reputation: 10074
With some straightforward LINQ:
var numbers = values.Where(x => { int num = 0; return Int32.TryParse(x, out num); })
.Select(num => Int32.Parse(num));
Notably this converts every string twice. Doing this imperatively you'll lose some clarity but gain some speed (as an IEnumerable extension):
public static IEnumerable<int> TryCastToInt<T>(this IEnumerable<T> values)
int num = 0;
foreach (object item in values) {
if (Int32.TryParse(item.ToString(), num)) {
yield return num;
}
}
}
Upvotes: 1
Reputation: 10407
int n;
var values = new[] { "", "1", "2", "a", "3" };
var intsonly = values.Where (v=> Int32.TryParse(v, out n)).Select(x => Int32.Parse(x));
Upvotes: 0
Reputation: 755537
You could try the following
public static IEnumerable<int> Convert(this IEnumerable<string> enumerable) {
Func<string,int?> convertFunc = x => {
int value ;
bool ret = Int32.TryParse(x, out value);
return ret ? (int?)value : null;
};
return enumerable
.Select(convertFunc)
.Where(x => x.HasValue)
.Select(x => x.Value);
}
This can easily then be converted to an array.
var numbers = values.Convert().ToArray();
Upvotes: 1