17

Given the variable

string ids = Request.QueryString["ids"]; // "1,2,3,4,5";

Is there any way to convert it into a List without doing something like

List<int> myList = new List<int>();

foreach (string id in ids.Split(','))
{
    if (int.TryParse(id))
    {
        myList.Add(Convert.ToInt32(id));
    }
}
Jason
  • 28,040
  • 10
  • 64
  • 64
Nick Allen
  • 11,970
  • 11
  • 45
  • 58
  • Can you specify the behaviour you want please - is it just as above, i.e., silently ignore bits that are not Int32s? None of the answers are taking that into account... – Ruben Bartelink May 26 '09 at 11:11
  • I am silently ignoring bad ids because they are entered by content editors who want to pull in certain images on to their pages, however I can put validation on the field to stop that from happening I suppose – Nick Allen May 26 '09 at 11:18
  • Yes, that's exactly what I was hinting at... – Ruben Bartelink May 26 '09 at 11:41
  • This almost-duplicate http://stackoverflow.com/questions/911717/split-string-convert-tolistint-in-one-line, from 6 hours later, specifically asks for a string of numbers, so it has the one simple answer. – goodeye Mar 06 '13 at 23:06
  • For more generic conversion http://stackoverflow.com/questions/28233115/comma-separated-string-to-generic-list – Gopi Jan 30 '15 at 11:01

6 Answers6

50

To create the list from scratch, use LINQ:

ids.Split(',').Select(i => int.Parse(i)).ToList();

If you already have the list object, omit the ToList() call and use AddRange:

myList.AddRange(ids.Split(',').Select(i => int.Parse(i)));

If some entries in the string may not be integers, you can use TryParse:

int temp;
var myList = ids.Split(',')
    .Select(s => new { P = int.TryParse(s, out temp), I = temp })
    .Where(x => x.P)
    .Select(x => x.I)
    .ToList();

One final (slower) method that avoids temps/TryParse but skips invalid entries is to use Regex:

var myList = Regex.Matches(ids, "[0-9]+").Cast<Match>().SelectMany(m => m.Groups.Cast<Group>()).Select(g => int.Parse(g.Value));

However, this can throw if one of your entries overflows int (999999999999).

Jason
  • 28,040
  • 10
  • 64
  • 64
  • I've edited my answer to reflect what's needed for an inline TryParse implementation. This is where a Convert.ToNullableInt32 implementation would be helpful :) – Jason May 26 '09 at 11:28
  • Yous sure that compiles (in LINQPad :D) – Ruben Bartelink May 26 '09 at 11:35
  • Yes. The only thing I'm not 100% sure on is the order of operations in the property initializer ('out temp' followed by I = temp). I believe that's guaranteed to be executed in order. – Jason May 26 '09 at 11:38
  • Agree it should work from exec order POV, but I hate temps shared by stuff further down like this. Extension/helper method is way to go thide all this cruft. – Ruben Bartelink May 26 '09 at 11:42
  • From a comment at this almost-duplicate Q&A http://stackoverflow.com/a/911729/292060, the `Select(i => int.Parse(i))` portion can be simplified to `Select(int.Parse)` – goodeye Mar 06 '13 at 22:56
8

This should do the trick:

myList.Split(',').Select(s => Convert.ToInt32(s)).ToList();

If the list may contain other data besides integers, a TryParse call should be included. See the accepted answer.

Ronald Wildenberg
  • 31,634
  • 14
  • 90
  • 133
4

Using Linq:

myList.AddRange(ids.Split(',').Select(s => int.Parse(s));

Or directly:

var myList = ids.Split(',').Select(s => int.Parse(s));

Also, to prevent the compiler from explicitly generating the (largely redundant) lambda, consider:

var myList = ids.Split(',').Select((Func<string, int>)int.Parse);

(Hint: micro-optimization.)

There's also TryParse which should be used instead of Parse (only) if invalid input is possible and should be handled silently. However, others have posted solutions using TryParse so I certainly won't. Just bear in mind that you shouldn't duplicate the calculation.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 2
    This way you get a list of booleans. Int32.TryParse returns a boolean. – Ronald Wildenberg May 26 '09 at 11:04
  • Another issue with the last 2 examples is actually that they return IEnumerable – kastermester May 26 '09 at 11:05
  • 1
    @Konrad, I jumped in and fixed that for you. Hope you don't mind ;) – LukeH May 26 '09 at 11:09
  • careful, TryParse is needed here – Ruben Bartelink May 26 '09 at 11:12
  • @Ruben, I've rolled back to Konrad's original answer, but I'm not really sure if that's preferable or not. Using TryParse means the code won't compile, using Parse means it'll compile but will cause an exception if/when it hits a bad ID at runtime. – LukeH May 26 '09 at 11:49
  • I'll take the compilation error on my site :P I'm sure Konrad will be along to return the correct result a la Jason and my answers! Man has this gotten out of hand! – Ruben Bartelink May 26 '09 at 12:00
  • Seriously, guys. First, thanks to Luke for editing my question. Why did you revert? The edit was spot-on – silly error on my part. `TryParse` of course has benefits but this is a completely different discussion. Especially @Ruben: please explain your +1 for TryParse … – Konrad Rudolph May 26 '09 at 14:07
  • @Konrad: A bit late but... At the time of your post, there were many answers that had provided answers that appeared simpler than yours. The problem is that they ignored a key part of the questioner's code - he was using TryParse to conditionally skip bad data. (See the comment "I am silently ignoring bad ids because they are entered by content editors who want to pull in certain images on to their pages, however I can put validation on the field to stop that from happening I suppose" on the question). The edit made your answer like all the others - semantically different. – Ruben Bartelink Oct 21 '09 at 08:41
  • On the other hand, in general I'd back the general suggestion to prefer stuff that throws (like `Parse`) whereever possible rather than adding conditional code that tries to be fault tolerant at the expense of making the code longer and adding loads of untested error handling. – Ruben Bartelink Oct 21 '09 at 08:45
3

Or including TryParse like in your example:

var res = ids.Split(',').Where(x => { int tmp; return int.TryParse(x, out tmp); }).Select(x => int.Parse(x)).ToList();
Dario
  • 48,658
  • 8
  • 97
  • 130
3

To match the request in terms of performance characteristics and behaviour, it should do the same thing and not go off doign regexes or not doing the 'TryParse':-

ds.Split(',')
  .Select( i => {
    int value; 
    bool valid = int.TryParse(out value); 
    return new {valid, value}
  })
  .Where(r=>r.valid)
  .Select(r=>r.value)
  .ToList();

But while correct, that's quite ugly :D

Borrowing from a hint in Jason's comment:-

ds.Split(',')
  .Select( i => {
    int value; 
    bool valid = int.TryParse(out value); 
    return valid ? new int?( value) : null;
  })
  .Where(r=>r != null)
  .Select(r=>r.Value)
  .ToList();

Or

static class Convert
{
  public static Int32? ConvertNullable(this string s)
  {
    int value; 
    bool valid = int.TryParse(out value); 
    return valid ? new int?( value) : null;
  }
}

ds.Split(',')
  .Select( s => Convert.ConvertNullable(s))
  .Where(r=>r != null)
  .Select(r=>r.Value)
  .ToList();
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • With inline declaration the part with `Select` could be slightly simpler `.Select(s => int.TryParse(s, out int d)? (int?)d : null)` – montonero Apr 22 '21 at 15:55
2

One issue at hand here is how we're gonna deal with values that are not integers (lets assume we'll get some that is not integers). One idea might be to simply use a regex:

^-?[0-9]+$

Now, we could combine all this up with (as shown in Konrad's example):

var myList = ids.Split(',').Where(s => Regex.IsMatch(s, "^-?[0-9]$")).Select(s => Convert.ToInt32(s)).ToList();

That should do the job.

kastermester
  • 3,058
  • 6
  • 28
  • 44
  • Regex isnt saying [1-9]+, but if it did, it could still be out of range and throw - not same as tryparse. – Ruben Bartelink May 26 '09 at 11:20
  • About out of range - very true. However, I don't see why you want to change the Regex to [1-9]+, this would turn the number "10" into an invalid number. Also I will modify the regex slightly to take into account of negative numbers. – kastermester May 26 '09 at 11:28
  • Sorry, meant [0-9], (really I meant \d). Was only making the "two problems" allegation, together with pointing out that the code is more complex and differnet with regex. Your regex still doesnt have a "+" after the [0-9] so will only work with 1 digit and hence never get out of raqnge :P – Ruben Bartelink May 26 '09 at 11:58