3

If I know there is only one matching item in a collection, is there any way to tell Linq about this so that it will abort the search when it finds it?

I am assuming that both of these search the full collection before returning one item?

var fred = _people.Where((p) => p.Name == "Fred").First();
var bill = _people.Where((p) => p.Name == "Bill").Take(1);

EDIT: People seem fixated on FirstOrDefault, or SingleOrDefault. These have nothing to do with my question. They simply provide a default value if the collection is empty. As I stated, I know that my collection has a single matching item.

AakashM's comment is of most interest to me. I would appear my assumption is wrong but I'm interested why. For instance, when linq to objects is running the Where() function in my example code, how does it know that there are further operations on its return value?

GazTheDestroyer
  • 20,722
  • 9
  • 70
  • 103
  • 4
    It depends on the underlying provider, but your assumption will be incorrect more often than not. – AakashM Apr 20 '12 at 10:11
  • 1
    Use FirstOrDefault, once it has found the item it will stop and not search the remaining items in the collection, otherwise if the item doesn't exist it will return null. If you want to throw an exception if there is more than one item then use SingleOrDefault, but this will search the entire selection. – Lloyd Powell Apr 20 '12 at 10:13
  • @AakashM: Have edited my question a bit. Can you comment at all? – GazTheDestroyer Apr 20 '12 at 10:40
  • 1
    Can't really do any better than to suggest reading Jon Skeet's [EduLinq](http://msmvps.com/blogs/jon_skeet/archive/tags/Edulinq/default.aspx) series of blog posts. As a whole it probably answers 90%+ of the LINQ questions on SO :) – AakashM Apr 20 '12 at 10:42
  • 2
    In this *particular* case, assuming LINQ-to-Objects, the implementation of `Where` in `Enumerable` in `System.Core` uses deferred execution: nothing is iterated until it is asked for, and `First` ends the iteration as soon as an element has been returned. – AakashM Apr 20 '12 at 10:48
  • Brill, thanks Aakash (especially for the link) – GazTheDestroyer Apr 20 '12 at 10:51

2 Answers2

7

Your assumption is wrong. LINQ uses deferred execution and lazy evaluation a lot. What this means is that, for example, when you call Where(), it doesn't actually iterate the collection. Only when you iterate the object it returns, will it iterate the original collection. And it will do it in a lazy manner: only as much as is necessary.

So, in your case, neither query will iterate the whole collection: both will iterate it only up to the point where they find the first element, and then stop.

Actually, the second query (with Take()) won't do even that, it will iterate the source collection only if you iterate the result.

This all applies to LINQ to objects. Other providers (LINQ to SQL and others) can behave differently, but at least the principle of deferred execution usually still holds.

svick
  • 236,525
  • 50
  • 385
  • 514
0

I think First() will not scan the whole collection. It will return immediatelly after the first match. But I suggest to use FirstOrDefault() instead.

EDIT:

Difference between First() and FirstOrDefault() (from MSDN):

The First() method throws an exception if source contains no elements. To instead return a default value when the source sequence is empty, use the FirstOrDefault() method.

Enumerable.First

Dummy01
  • 1,985
  • 1
  • 16
  • 21