tags:

views:

589

answers:

6

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));
    }
}
+2  A: 

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
This way you get a list of booleans. Int32.TryParse returns a boolean.
Ronald Wildenberg
Another issue with the last 2 examples is actually that they return IEnumerable<T>
kastermester
@Konrad, I jumped in and fixed that for you. Hope you don't mind ;)
LukeH
careful, TryParse is needed here
Ruben Bartelink
@Luke, you should un-edit....
Ruben Bartelink
+1 for the original answer using TryParse
Ruben Bartelink
@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
I'll take the compilation error on my site :PI'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
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
@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
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
+19  A: 

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
But it's not resilient (doesnt do a TryParse)....
Ruben Bartelink
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
Yous sure that compiles (in LINQPad :D)
Ruben Bartelink
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
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
Removed -1 as no longer wrong.
Ruben Bartelink
+5  A: 

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
same as accepted?
Ruben Bartelink
Correct, think we came up with the same answer at the same time...
Ronald Wildenberg
Well then, same comment re not using TryParse :P (But undoing -1!)
Ruben Bartelink
Yes, TryParse was not included and since the data is a query string it should be there. But thanks for undoing the -1 :)
Ronald Wildenberg
A: 

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
Regex isnt saying [1-9]+, but if it did, it could still be out of range and throw - not same as tryparse.
Ruben Bartelink
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
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
+1  A: 

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
+2  A: 

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
That's the correct solution!
Dario