7

Possible Duplicate:
LINQ: Select parsed int, if string was parseable to int

This could be a basic question, but I couldn't figure out a work around. I have an array of strings and I tried to parse them with integers. As expected I got Format Exception.

How could I skip "3a" and proceed parsing the remaining array and storing the integers into output using Linq.? Is this a better approach or a DON'T DO practice? Pls shed some light on how to use TryParse in this case

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] values = { "1", "2", "3a","4" };
            List<int> output = new List<int>();

            try{
                output = values.Select(i => int.Parse(i)).ToList<int>();
            }
            catch(FormatException)
            {
                foreach (int i in output)
                    Console.WriteLine(i);
            }

            foreach (int i in output)
                Console.WriteLine(i);

            Console.ReadLine();
        }

    }
}
Community
  • 1
  • 1
Naga Sandeep
  • 1,421
  • 2
  • 13
  • 26

6 Answers6

7

You can use int.TryParse

string[] values = { "1", "2", "3a","4" };
int i = int.MinValue;
List<int> output = values.Where(s => int.TryParse(s, out i))
                         .Select(s => i)
                         .ToList();

Demo

However, Eric Lippert would not be amused. So if you don't want to (ab)use side effects, this would be the best-practise approach:

Create an extension method like:

public static class NumericExtensions
{
    public static int? TryGetInt(this string item)
    {
        int i;
        bool success = int.TryParse(item, out i);
        return success ? (int?)i : (int?)null;
    }
}

Then you are able to write this:

List<int> output = values.Select(s => s.TryGetInt())
             .Where(nullableInt => nullableInt.HasValue)
             .Select(nullableInt => nullableInt.Value)
             .ToList();
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • Is this guaranteed to work? I think there is no technical reason why `Where` cannot overwrite `i` with the second value before `Select` reads the first. –  Jan 22 '13 at 13:11
  • @hvd: Yes, it is, at least in Linq-To-Objects. Although i have currently no link available. – Tim Schmelter Jan 22 '13 at 13:13
  • @hvd: This answer relies on the implementation detail "defered execution" of LINQ to Objects. In its current implementation and when using LINQ to Objects it is guaranteed to work. – Daniel Hilgarth Jan 22 '13 at 13:14
  • All I see in the [documentation](http://msdn.microsoft.com/en-us/library/bb534803.aspx) is "This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic." But that allows the filter to be evaluated for all records in one go even if the current implementation doesn't do so. –  Jan 22 '13 at 13:16
  • I was initially surprised by this code, but after a little thought about 'deferred execution' I now understand it. – Colonel Panic Jan 22 '13 at 13:20
  • @hvd: That's why I am saying it is an implementation detail. Having said that, I guess it is impossible to have deferred execution and execute the `Where` for all elements before executing the first `Select`. – Daniel Hilgarth Jan 22 '13 at 13:20
  • Here's what J. Skeet would do if you don't want a variable outside of the query: http://stackoverflow.com/a/4961697/284240 – Tim Schmelter Jan 22 '13 at 13:28
  • @hvd: Point taken, updated my answer to provide another approach using an extension which returns a `Nullable`. Edit: Only just seen that you've already answered similarily. – Tim Schmelter Jan 22 '13 at 14:23
  • how about `!string.IsNullOrWhiteSpace` sir? because we are to casting the `string[]` to List. – spajce Jan 22 '13 at 14:46
  • @spajce: How should it help to prevent `"3a"` from being misinterpreted as `int`? – Tim Schmelter Jan 22 '13 at 14:51
  • i thought that `string[]` is just an example. what if in that `string[]` has a `null` or `empty` space :) – spajce Jan 22 '13 at 14:53
  • okay sir.. i figured it out, i forgot the `int.TryParse` :) – spajce Jan 22 '13 at 14:55
2

Although I fully agree with the use of int.TryParse in Tim Schmelter's answer, I think his answer relies on an undocumented implementation detail, and a safer alternative might be

List<int> output =
    values
    .Select(s => {
        int i;
        return int.TryParse(s, out i) ? i : default(int?);
    })
    .Where(i => i != null)
    .Select(i => i.Value)
    .ToList();

You might be able to replace the .Where(...).Select(...) with .OfType<int>().

You might also put the first .Select(...) lambda with an explicit reusable function:

int? MyTryParse(string s)
{
    int i;
    return int.TryParse(s, out i) ? i : default(int?);
}

List<int> output =
    values
    .Select(MyTryParse)
    .Where(i => i != null)
    .Select(i => i.Value)
    .ToList();
2

linq version from answer of Tim Schmelter

        string[] values = { "1", "2", "3a", "4" };
        int i = int.MinValue;
        var output = (from c in values
                      where int.TryParse(c, out i) 
                      select c).Select(s => int.Parse(s)).ToList();
        foreach (var item in output)
        {
            Console.WriteLine(item.ToString());
        }
Community
  • 1
  • 1
spajce
  • 7,044
  • 5
  • 29
  • 44
1

Why you want to use LINQ?

Try this:

foreach(string str in values)
{
   int val;
   if(int.TryParse(str, out val))
   {
      output.Add(val);
   }
}
Hamlet Hakobyan
  • 32,965
  • 6
  • 52
  • 68
1

How about this? Inspired by Tim's answer, but with the temporary variable moved inside the loop, so it's parallel-safe (suppose the collections of strings values were a ParallelEnumerable).

values.Select(s =>
    {int i; return int.TryParse(s, out i) ? (int?)i : null;})
    .Where(x=>x!=null).Select(x=>x.Value);

So given ["1", "two", "3"] it returns [1,3]

Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
0
List<string> output = values.Where(v => Regex.Match(v, "^(0|[1-9][0-9]*)$").Success)
                                        .ToList();

Example using Regex for more control to dictate which values are to be deemed valid.

Caster Troy
  • 2,796
  • 2
  • 25
  • 45