8

I have a List<string>, and some of these strings are numbers. I want to extract this subset into a List<int>.

I have done this in quite a verbose looking way - as below - but I get the feeling there must be a neater LINQ way to structure this. Any ideas?

List<string> myStrs = someListFromSomewhere;
List<int> myInts = new List<int>();

foreach (string myStr in myStrs)
{
    int outInt;
    if (int.TryParse(myStr, out outInt))
    {
        myInts.Add(outInt);
    }
}

Obviously I don't need a solution to this - it's mainly for my LINQ education.

jlb83
  • 1,988
  • 1
  • 19
  • 30
  • 2
    Another occasion for me to state 'I wish TryParse would return an int?' (I know, legacy...) – vc 74 Jan 18 '12 at 13:29
  • certainly not more efficient, but you could do `var myInts = myStrs.Where(s => int.TryParse(s, out outInt)).Select(s => int.Parse(s))`, as long as you already had outInt defined. This calls `TryParse` *and* Parse on each string, though - so I wouldn't really suggest it – rejj Jan 18 '12 at 13:30
  • 2
    This question might be of interest: [LINQ query to perform a projection, skipping cases where the projection would cause an exception](http://stackoverflow.com/questions/7188623/linq-query-to-perform-a-projection-skipping-cases-where-the-projection-would-ca) I use your exact case as an example. – George Duckett Jan 18 '12 at 13:51

4 Answers4

24

You can do it like this:

int parsed = 0;

myInts = myStrs.Where(x => int.TryParse(x, out parsed)).Select(x => parsed);

This works because the execution of LINQ operators is deferred, meaning:
For each item in myStrs first the code in Where is executed and the result written into parsed. And if TryParse returned true the code in Select is executed. This whole code for one item runs before this whole code is run for the next item.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
7

LINQ and out parameters don't mix well. You could do this:

var myInts = myStrs
    .Select(s =>
    {
        int outInt;
        return int.TryParse(s, out outInt) ? (int?)outInt : null;
    })
    .Where(i => i.HasValue)
    .Select(i => i.Value)
    .ToList();
dtb
  • 213,145
  • 36
  • 401
  • 431
  • @DanielHilgarth: yes, might be a bit overkill, but it will also be very clear. I have met more developers that don't quite grasp the effects of deferred execution (or are even aware of its existence) than those who do. – Fredrik Mörk Jan 18 '12 at 13:31
  • 2
    @FredrikMörk: Fair enough. But in that case, don't use LINQ at all and use the classic loop the OP already has. It isn't more code and it is even more readable than the solution provided by dtb. – Daniel Hilgarth Jan 18 '12 at 13:33
  • @DanielHilgarth: I see your point, and it's a good one. I usually think that learning comes in steps. Moving into LINQ potentially involves many new concepts (such as lambdas, anonymous methods, deferred execution). Your (very elegant, btw) solution uses many of these at the same time. That could be good, but it could also be overwhelming depending on the reader's starting point. I'm not saying you are wrong (quite the opposite), but as a learning tool, this could very well be a valid step. – Fredrik Mörk Jan 18 '12 at 13:39
  • @FredrikMörk: I agree with what you say. My main concern when posting that comment was that someone sees this answer and takes it as the best way to do it, especially when it comes from a respected member with that much reputation. Using LINQ this is not the case, it can be done better (= shorter, easier to understand *if* you understand LINQ). Indeed, it is a valid step in learning LINQ, but only an intermediate one and not the last. – Daniel Hilgarth Jan 18 '12 at 13:46
0

Select distinct then parse each selected item

List<int> myInts = new List<int>();
myInts = myStrs.Distinct().Select(
    x =>
    {
        int parsed;
        int.TryParse(x, out parsed);
        return parsed;
    })  
.ToList();
Ahmed Ghoniem
  • 661
  • 1
  • 8
  • 15
0

Since this is for LINQ education... If you really are looking for how this can be done with only LINQ syntax, Another option is to move the parsing logic to a class that encapsulates the result and the value.

var myInts = from val in myStrs
             let parserVal = new Parser(val)
             where parserVal.IsInt
             select parserVal.Val;

where Parser is something like this...

class Parser
{
        public bool IsInt { get; set; }
        public int Val { get; set; }

        public Parser(string val)
        {
            int outVal;
            IsInt = int.TryParse(val, out outVal);
            if (IsInt)
            {
                Val = outVal;
            }
        }
}
Kerry H
  • 661
  • 5
  • 7