10

Let's say I have a web page that currently accepts a single ID value via a url parameter:
http://example.com/mypage.aspx?ID=1234

I want to change it to accept a list of ids, like this:
http://example.com/mypage.aspx?IDs=1234,4321,6789

So it's available to my code as a string via context.Request.QueryString["IDs"]. What's the best way to turn that string value into a List<int>?

Edit: I know how to do .split() on a comma to get a list of strings, but I ask because I don't know how to easily convert that string list to an int list. This is still in .Net 2.0, so no lambdas.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794

13 Answers13

13

No offense to those who provided clear answers, but many people seem to be answering your question instead of addressing your problem. You want multiple IDs, so you think you could this this:

http://example.com/mypage.aspx?IDs=1234,4321,6789

The problem is that this is a non-robust solution. In the future, if you want multiple values, what do you do if they have commas? A better solution (and this is perfectly valid in a query string), is to use multiple parameters with the same name:

http://example.com/mypage.aspx?ID=1234;ID=4321;ID=6789

Then, whatever query string parser you use should be able to return a list of IDs. If it can't handle this (and also handle semi-colons instead of ampersands), then it's broken.

Ovid
  • 11,580
  • 9
  • 46
  • 76
  • 1
    asp.net has no issues with page.aspx?id=1,2,3,4&otherid=4,5,6,7 so I fail to see why a comma separated list is a non-robust solution. – Robert Paulson Oct 13 '08 at 22:15
  • 1
    Because, as stated, if the *values* are allowed to contain commas, splitting on commas is problematic. The CGI protocol allows multiple parameters of the same name, so the entire "can I split on commas or not" question becomes moot. That's what duplicate parameter names are for. – Ovid Oct 14 '08 at 10:02
  • I think if alphanumeric IDs or GUIDs are listed, a comma won't appear inside the IDs, and from that point of view commas are a robust solution? But there might be other issues I suppose, e.g. Wiki syntax parsers that believes links end when there is a comma? – KajMagnus Apr 16 '11 at 11:37
  • 1
    this is a horrible idea! and what if in the future the values contant ";"? ";" is no better than "," - if you want to be punctual, both are dis-allowed by the rfc – Nas Banov May 02 '11 at 22:40
  • 1
    @Nas: sorry, but that is not correct. A semicolon (;) in a string which is not intended to be a separator is REQUIRED to be uri escaped (as %3B in this case) as it's a reserved character. See section 2.2 of RFC 2396 - http://www.ietf.org/rfc/rfc2396.txt. Any query string parser which does not handle this is broken by definition. You are correct that ',' is also a reserved character and must be escaped, but you can't tell if it's escaped to separate or escaped because it's a valid value. – Ovid May 18 '11 at 18:34
  • This is a very elegant and informative solution. I didn't know that it could be done like this. – Richard C Jun 15 '12 at 07:29
12

Something like this might work:

public static IList<int> GetIdListFromString(string idList)
{
    string[] values = idList.Split(',');

    List<int> ids = new List<int>(values.Length);

    foreach (string s in values)
    {
        int i;

        if (int.TryParse(s, out i))
        {
            ids.Add(i);
        }
    }

    return ids;
}

Which would then be used:

string intString = "1234,4321,6789";

IList<int> list = GetIdListFromString(intString);

foreach (int i in list)
{
    Console.WriteLine(i);
}
Compile This
  • 11,892
  • 2
  • 25
  • 22
4

You can instantiate a List<T> from an array.

VB.NET:

Dim lstIDs as new List(of Integer)(ids.split(','))

This is prone to casting errors though if the array contains non-int elements

user7658
  • 964
  • 8
  • 7
  • The problem with this is that just one casting error kills the entire list, but I still up-voted it, and maybe that's the behavior I should go for. – Joel Coehoorn Sep 15 '08 at 14:50
2

All I can think of is to loop over the list of strings (which you have got from performing a split) and doing something like int.TryParse() on them one after the other and putting them into a new List<int>. Encapsulate it in a nice little helper method somewhere and it won't be too horrid.

Mark Embling
  • 12,605
  • 8
  • 39
  • 53
2

If you like the functional style, you can try something like

    string ids = "1,2,3,4,5";

    List<int> l = new List<int>(Array.ConvertAll(
        ids.Split(','), new Converter<string, int>(int.Parse)));

No lambdas, but you do have Converters and Predicates and other nice things that can be made from methods.

Jesse Millikan
  • 3,104
  • 1
  • 21
  • 32
2

I see my answer came rather late, i.e. several other had written the same. Therefore I present an alternative method using regular expressions to validate and divide the string.

class Program
{
    //Accepts one or more groups of one or more digits, separated by commas.
    private static readonly Regex CSStringPattern = new Regex(@"^(\d+,?)*\d+$");

    //A single ID inside the string. Must only be used after validation
    private static readonly Regex SingleIdPattern = new Regex(@"\d+");

    static void Main(string[] args)
    {
        string queryString = "1234,4321,6789";

        int[] ids = ConvertCommaSeparatedStringToIntArray(queryString);
    }

    private static int[] ConvertCommaSeparatedStringToIntArray(string csString)
    {
        if (!CSStringPattern.IsMatch(csString))
            throw new FormatException(string.Format("Invalid comma separated string '{0}'",
                                                    csString));

        List<int> ids = new List<int>();
        foreach (Match match in SingleIdPattern.Matches(csString))
        {
            ids.Add(int.Parse(match.Value)); //No need to TryParse since string has been validated
        }
        return ids.ToArray();
    }
}
Community
  • 1
  • 1
Magnus Akselvoll
  • 1,257
  • 1
  • 11
  • 16
1

split is the first thing that comes to mind, but that returns an array, not a List; you could try something like:


List<int> intList = new List<int>;

foreach (string tempString in ids.split(',')
{
    intList.add (convert.int32(tempString));
}

Richard C
  • 2,176
  • 5
  • 30
  • 40
1

Final code snippet that takes what I hope is the best from all the suggestions:

Function GetIDs(ByVal IDList As String) As List(Of Integer)
    Dim SplitIDs() As String = IDList.Split(new Char() {","c}, StringSplitOptions.RemoveEmptyEntries)
    GetIDs = new List(Of Integer)(SplitIDs.Length)
    Dim CurID As Integer
    For Each id As String In SplitIDs
        If Integer.TryParse(id, CurID) Then GetIDs.Add(CurID)
    Next id
End Function

I was hoping to be able to do it in one or two lines of code inline. One line to create the string array and hopefully find something in the framework I didn't already know to handle importing it to a List<int> that could handle the cast intelligently. But if I must move it to a method then I will. And yes, I'm using VB. I just prefer C# for asking questions because they'll get a larger audience and I'm just about as fluent.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
0

You can use string.Split() to split the values once you have extracted them from the URL.

string[] splitIds = ids.split(',');
Grokys
  • 16,228
  • 14
  • 69
  • 101
0

You'll just have to foreach through them and int.TryParse each one of them. after that just add to the list.

Nevermind - @Splash beat me to it

sirrocco
  • 7,975
  • 4
  • 59
  • 81
0
List<int> convertIDs = new List<int>;
string[] splitIds = ids.split(',');
foreach(string s in splitIds)
{
    convertIDs.Add(int.Parse(s));
}

For completeness you will want to put try/catches around the for loop (or around the int.Parse() call) and handle the error based on your requirements. You can also do a tryparse() like so:

List<int> convertIDs = new List<int>;
string[] splitIds = ids.split(',');
foreach(string s in splitIds)
{
    int i;
    int.TryParse(out i);
    if (i != 0)
       convertIDs.Add(i);
}
dpollock
  • 185
  • 1
  • 4
0

To continue on previous answer, quite simply iterating through the array returned by Split and converting to a new array of ints. This sample below in C#:

        string[] splitIds = stringIds.Split(',');

        int[] ids = new int[splitIds.Length];
        for (int i = 0; i < ids.Length; i++) {
            ids[i] = Int32.Parse(splitIds[i]);
        }
Philibert Perusse
  • 4,026
  • 5
  • 24
  • 26
0

I think the easiest way is to split as shown before, and then loop through the values and try to convert to int.

class Program
{
    static void Main(string[] args)
    {
        string queryString = "1234,4321,6789";

        int[] ids = ConvertCommaSeparatedStringToIntArray(queryString);
    }

    private static int[] ConvertCommaSeparatedStringToIntArray(string csString)
    {
        //splitting string to substrings
        string[] idStrings = csString.Split(',');

        //initializing int-array of same length
        int[] ids = new int[idStrings.Length];

        //looping all substrings
        for (int i = 0; i < idStrings.Length; i++)
        {
            string idString = idStrings[i];

            //trying to convert one substring to int
            int id;
            if (!int.TryParse(idString, out id))
                throw new FormatException(String.Format("Query string contained malformed id '{0}'", idString));

            //writing value back to the int-array
            ids[i] = id;
        }

        return ids;
    }
}
Magnus Akselvoll
  • 1,257
  • 1
  • 11
  • 16