Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq query to select single string from multiple List<string>

Tags:

string

c#

linq

I am having a hard time understanding why I am getting the results that I am.

I have two lists of strings:

var list1 = new List<String> {"item 1", "item 2"};
var list2 = new List<String> { "item 3", "item 4" };

Version 1 - Output: "item 2"

var item =
            from x in (list1.Concat(list2))
            where x.EndsWith("2")
            select x;

        Console.WriteLine(item.First());

Version 2 - Output: "i"

var item =
            from x in (list1.Concat(list2))
            where x.EndsWith("2")
            select x.First();

        Console.WriteLine(item.First());

Version 3 - Output: "System.Linq.Enumerable+WhereEnumerableIterator`1[System.String]"

var item =
            from x in (list1.Concat(list2))
            where x.EndsWith("2")
            select x;

        Console.WriteLine(item);

Given that version 2 outputs "i", I would expect version 3 to output "item 2". Why is this behavior occurring?

like image 533
Joe Bauer Avatar asked Mar 19 '23 12:03

Joe Bauer


1 Answers

In version 3, select x is returning a sequence of strings that match your critera; it just happens to be a sequence with one item in it.

Console.WriteLine internally calls .ToString() on whatever you pass into it. Since there is no meaningful string representation for IEnumerable<T>, the default in .NET is to print the string name of the type.

Based on your wording, I think part of your confusion does come from a misunderstanding of why version 2 works the way it does. In version 2, select x.First() is actually a bit of a quirk/coincidence, because a string is also an IEnumerable<char>, so you can do LINQ operations on the string. .First() returns the first element of that char sequence, for each result that matches your criteria. So you're saying:

"For each element which matches my criteria, select the first character, and then return the sequence of all the first characters for the matches."

So in fact, item in version 2 is an IEnumerable<char> with one element in it. Calling Console.WriteLine() on an IEnumerable<char> will just print the chars in order. So you get "i".

(note: I see after I answered this, the question was edited to call .First() both inside the projection and on the result, so the bit about passing IEnumerable<char> to Console.WriteLine isn't totally relevant anymore)

Keep in mind LINQ is pretty much about working with sets until you explicitly reduce them down. For example, Select is simply a projection or transformation. It returns the same number of items that were passed to it, transformed. Where reduces down the set, but it's still a set.

like image 55
Rex M Avatar answered May 01 '23 16:05

Rex M