4

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?

6 Answers 6

5

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.

Sign up to request clarification or add additional context in comments.

1 Comment

+1 This is the only other answer I've seen that recognizes the question is about the 3rd case, and not the 2nd
2

Your Version 2 is selecting first item/char from the string x.First(), whereas your first version is selecting first item from the result set-> First string.

Version 1 is like - Select First Item from the Result Set

var item =  (from x in (list1.Concat(list2))
            where x.EndsWith("2")
            select x).First(); //First complete string will be selected

and version 2 is like- Select First Item from a string in result set

var item =  from x in (list1.Concat(list2))
            where x.EndsWith("2")
            select x.First(); //only first char will be selected for string

Your third case is selecting an IEnumerable<string>, so when you call Console.WriteLine, it calls the default implementation of ToString and thus you get

"System.Linq.Enumerable+WhereEnumerableIterator`1[System.String]"

3 Comments

Your examples are very helpful here. So, will a select without a .First() or .Single() always return a set as opposed to a single element?
@JoeBauer, yes. Exactly.
@JoeBauer, one more thing, Even if there are no rows matching the criteria select would return an empty collection/set.
0

When using First() you are materializing the list, causing the iterator to execute. That is eager execution. Your third version uses select which does not materialize the list abd uses Defeered Execution which returns an iterator, hence calling ToString() on it returning the iterators name

Comments

0

Because Where returns an IEnumerable.

You have written the equivalent of:

var whoKnows = list1.Concat(list2).Where(x => x.EndsWith("2"));
Console.WriteLine(whoKnows);

The ToString of a collection just returns the class name.

Comments

0

The type of the x in your Version 2 is String. The type of item in Version 1 is IEnumerable.

So your Version 2 return a list of characters which are the first characters of each string. In your Version 1, item.First() return the first element of the result set which is a string.

Comments

0
//This raise a exception if no item found
var item=list1.Concat(list2).First(i => i.EndsWith("2"));
//this return default value (null) if no item found
var item2 = list1.Concat(list2).FirstOrDefault(i => i.EndsWith("2"));

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.