Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# LINQ: How is string("[1, 2, 3]") parsed as an array?

Tags:

c#

linq

I am trying to parse a string into array and find a very concise approach.

string line = "[1, 2, 3]";
string[] input = line.Substring(1, line.Length - 2).Split();
int[] num = input.Skip(2)
                 .Select(y => int.Parse(y))
                 .ToArray();

I tried remove Skip(2) and I cannot get the array because of non-int string. My question is that what is the execution order of those LINQ function. How many times is Skip called here?

Thanks in advance.

like image 337
David Avatar asked Jan 09 '23 18:01

David


2 Answers

The order is the order that you specify. So input.Skip(2) skips the first two strings in the array, so only the last remains which is 3. That can be parsed to an int. If you remove the Skip(2) you are trying to parse all of them. That doesn't work because the commas are still there. You have splitted by white-spaces but not removed the commas.

You could use line.Trim('[', ']').Split(','); and int.TryParse:

string line = "[1, 2, 3]";
string[] input = line.Trim('[', ']').Split(',');
int i = 0;
int[] num = input.Where(s => int.TryParse(s, out i)) // you could use s.Trim but the spaces don't hurt
                 .Select(s => i)
                 .ToArray(); 

Just to clarify, i have used int.TryParse only to make sure that you don't get an exception if the input contains invalid data. It doesn't fix anything. It would also work with int.Parse.

Update: as has been proved by Eric Lippert in the comment section using int.TryParse in a LINQ query can be harmful. So it's better to use a helper method that encapsulates int.TryParse and returns a Nullable<int>. So an extension like this:

public static int? TryGetInt32(this string item)
{
    int i;
    bool success = int.TryParse(item, out i);
    return success ? (int?)i : (int?)null;
}

Now you can use it in a LINQ query in this way:

string line = "[1, 2, 3]";
string[] input = line.Trim('[', ']').Split(',');
int[] num = input.Select(s => s.TryGetInt32())
                 .Where(n => n.HasValue)
                 .Select(n=> n.Value)
                 .ToArray();
like image 128
Tim Schmelter Avatar answered Jan 16 '23 04:01

Tim Schmelter


The reason it does not work unless you skip the first two lines is that these lines have commas after ints. Your input looks like this:

"1," "2," "3"

Only the last entry can be parsed as an int; the initial two will produce an exception.

Passing comma and space as separators to Split will fix the problem:

string[] input = line
    .Substring(1, line.Length - 2)
    .Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries);

Note the use of StringSplitOptions.RemoveEmptyEntries to remove empty strings caused by both comma and space being used between entries.

like image 34
Sergey Kalinichenko Avatar answered Jan 16 '23 04:01

Sergey Kalinichenko