4

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.

10
  • 1
    What do you mean "it does not work anymore" after you removed the Skip()? What was it doing with it that it is not doing without it? Commented Mar 11, 2015 at 13:58
  • 1
    Instead of using substring which can fail you should use .Trim('['],). The Split also doesn't work anymore if you remove the spaces, for example: 1,2,3 Commented Mar 11, 2015 at 13:58
  • @TimSchmelter I think you meant ('[', ']') Commented Mar 11, 2015 at 14:00
  • 1
    You need to split with , not just Split. Commented Mar 11, 2015 at 14:00
  • Skip(2) is called, then the object returned executes de Select() statement, and then the object returned from the Select is converted to an array. Commented Mar 11, 2015 at 14:01

4 Answers 4

6

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();
Sign up to request clarification or add additional context in comments.

16 Comments

Very concise and yet still detailed answer, very well answered
I strongly recommend against using out parameters in LINQ queries like this. Though this particular usage happens to be safe, you can easily make minor changes that get you into situations where the variable is mutated multiple times and later parts of the query are looking at the current value of the variable, not at the value you want.
Sure. Suppose we have a sequence var seq = new List<string> { "1", "blah", "3" }; We wish to extract a sequence of the numbers, and if one of them is not a number, use zero instead. Easily done var nums = from item in seq let success = int.TryParse(item, out tmp) select success ? tmp : 0; Looks fine, right? Now what happens if we make a small edit and say "and I wish to order by the item". Predict the result of executing the query from item in seq let success = int.TryParse(item, out tmp) orderby item select success ? tmp : 0; -- does your prediction match reality?
Your plausible but incorrect belief is exactly why this pattern is dangerous. This query actually means "produce a series of pairs and side effects. The first pair is {"1", true} and the side effect is tmp = 1. The second pair is {"blah", false} and the side effect is tmp = 0. The third pair is {"3", true} and the side effect is tmp = 3. Now order those pairs by the first item. Now for each of those pairs in the ordered sequence, check the second item in the pair, and if it is false, produce zero, and if it is true, produce the current value of tmp" . The current value of tmp is 3.
@TimSchmelter: The developer time to implement the method is five minutes. But the method needs a specification -- sure, it's a short one, but testing and documentation is going to need the spec to work from. Then you write the tests, and the documentation, and then you translate the documentation into Japanese, and ... and it all adds up into a lot of work. All of that is work that is not being spent on more expensive features that have a much greater return on investment. Like I said, there are no inexpensive features, only less expensive features.
|
3

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.

Comments

0

I think it would be better you do it this way:

JsonConvert.DeserializeObject(line, typeof(List<int>));

Comments

0

you might try

    string line = "[1,2,3]";
    IEnumerable<int> intValues = from i in line.Split(',')
                                 select Convert.ToInt32(i.Trim('[', ' ', ']'));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.