myCars.TakeWhile(car => !myCondition(car)).Count();
It works! Think about it. The index of the first matching item equals the number of (not matching) item before it.
Story time
I too dislike the horrible standard solution you already suggested in your question. Like the accepted answer I went for a plain old loop although with a slight modification:
public static int FindIndex<T>(this IEnumerable<T> items, Predicate<T> predicate) {
int index = 0;
foreach (var item in items) {
if (predicate(item)) break;
index++;
}
return index;
}
Note that it will return the number of items instead of -1
when there is no match. But let's ignore this minor annoyance for now. In fact the horrible standard solution crashes in that case and I consider returning an index that is out-of-bounds superior.
What happens now is ReSharper telling me Loop can be converted into LINQ-expression. While most of the time the feature worsens readability, this time the result was awe-inspiring. So Kudos to the JetBrains.
Analysis
Pros
- Concise
- Combinable with other LINQ
- Avoids
new
ing anonymous objects
- Only evaluates the enumerable until the predicate matches for the first time
Therefore I consider it optimal in time and space while remaining readable.
Cons
- Not quite obvious at first
- Does not return
-1
when there is no match
Of course you can always hide it behind an extension method. And what to do best when there is no match heavily depends on the context.