Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shove a delimited string into a List<int>

Tags:

string

c#

parsing

If I have for example the following string:

"123;3344;4334;12"

and I want these numbers in a generic List<int>, I guess I don't know of a good way here other than to split in a loop and do a conversion then add to a List<int> through each iteration. Does anyone have other ways to go about this?

Updated. Here's what I came up with. I want to do this the old fashion way, not with LINQ because I'm trying to get better with just strings, arrays, lists and manipulating and converting in general.

public List<int> StringToList(string stringToSplit, char splitDelimiter)
{
    List<int> list = new List<int>();

    if (string.IsNullOrEmpty(stringToSplit))
        return list;

    string[] values = stringToSplit.Split(splitDelimiter);

    if (values.Length <= 1)
        return list;

    foreach (string s in values)
    {
        int i;
        if (Int32.TryParse(s, out i))
            list.Add(i);
    }

    return list;
}

This is a new string utility method I plan on using whenever I need to convert a delimited string list to List

So I'm returning an empty list back to the caller if something fails. Good/Bad? is it pretty common to do this?

Yes, there are more "elegant" ways to do this with LINQ but I want to do it manually..the old way for now just for my own understanding.

Also, what bothers me about this:

list.AddRange(str.Split(';').Select(Int32.Parse));

is that I have no idea:

  1. How to shove in a TryParse there instead.
  2. What if the str.Split(';').Select(Int32.Parse) just fails for whatever reason...then the method that this AddRange resides in is going to blow up and unless I add a try/catch around this whole thing, I'm screwed if I don't handle it properly.
like image 909
PositiveGuy Avatar asked Jun 29 '10 01:06

PositiveGuy


2 Answers

static int? ToInt32OrNull(string s)
{
    int value;
    return (Int32.TryParse(s, out value)) ? value : default(int?);      
}
// ...
var str = "123;3344;4334;12"; 
var list = new List<int>();
list.AddRange(str.Split(';')
                 .Select(ToInt32OrNull)
                 .Where(i => i != null)
                 .Cast<int>());

Questioner notes:

I don't know of a good way here other than to split in a loop and do a conversion then add to a List

In general, this is a major reason why LINQ was brought into C# - to remove the need to work with sequences of values by implementing loops, and instead just declare your intention to transform the sequence. If you ever find yourself thinking "I don't know how to do this except with a loop" - it's time to look into a LINQ construct which will do the work for you.

Performance Update:

Performance of LINQ has been quesioned below. While in the comments the idea of LINQ being slower is defended since we gain the benefits of readability, maintainability and composibility, there is another aspect which gives LINQ an easy performance advantage: parallelism. Here is an example where adding just one extension method call, AsParallel() doubles the performance. This is a great example of where scale-out beats micro-optimization without even needing to measure very carefully. Note I'm not claiming that micro-optimizations are not ever needed, but with the tools we have available at this level of absraction, the need becomes vanishingly small.

class Program
{
    private const int ElementCount = 10000000;

    static void Main(string[] args)
    {
        var str = generateString();
        var stopwatch = new Stopwatch();

        var list1 = new List<int>(ElementCount); 
        var list2 = new List<int>(ElementCount);

        var split = str.Split(';');

        stopwatch.Start();
        list1.AddRange(split
                          .Select(ToInt32OrNull) 
                          .Where(i => i != null) 
                          .Cast<int>());
        stopwatch.Stop();

        TimeSpan nonParallel = stopwatch.Elapsed;

        stopwatch.Restart();

        list2.AddRange(split
                          .AsParallel()
                          .Select(ToInt32OrNull)
                          .Where(i => i != null)
                          .Cast<int>());

        stopwatch.Stop();

        TimeSpan parallel = stopwatch.Elapsed;

        Debug.WriteLine("Non-parallel: {0}", nonParallel);
        Debug.WriteLine("Parallel: {0}", parallel);
    }

    private static String generateString()
    {
        var builder = new StringBuilder(1048576);
        var rnd = new Random();

        for (int i = 0; i < ElementCount; i++)
        {
            builder.Append(rnd.Next(99999));
            builder.Append(';');
        }

        builder.Length--;

        return builder.ToString();
    }

    static int? ToInt32OrNull(string s)
    {
        int value;
        return (Int32.TryParse(s, out value)) ? value : default(int?);
    } 
}

Non-parallel: 00:00:07.0719911

Parallel: 00:00:04.5933906

like image 119
codekaizen Avatar answered Oct 29 '22 09:10

codekaizen


string str = "123;3344;4334;12";
List<int> list = new List<int>();

foreach (string s in str.Split(';'))
{
    list.Add( Int32.Parse(s));
}
like image 26
David Avatar answered Oct 29 '22 11:10

David