Saturday 4 February 2012

Linq IndexOfFirst

Well, time for a very simple post (not that my posts tend to be complex, but this is even more basic stuff). The other day I needed to obtain the index of the fist item in one collection that satisfies some condition. Oddly enough, there's not any method for that in the System.Linq.Enumerable class, so you'll have to write your own.
While searching the net to confirm that I was not missing something, I found that the solution used by many people was doing a Select of the given collection generating the initial items plus an index, and then applying the condition to that new collection and getting the index. Well, I better show the code to see how simple that is (I guess my explanation above is somewhat confusing)

public static int IndexOfFirst1<T>(this IEnumerable<T> items, Predicate<T> condition)
 {
  return items.Select((it, index) => new { Item = it, Index = index}).First(indexedIt => condition(indexedIt.Item)).Index;
 }

Well, if you're not aware of that overload of the Select method that expects an index, you could use the normal Select plus the power of closures like this:

public static int IndexOfFirst2<T>(this IEnumerable<T> items, Predicate<T> condition)
 {
   int i = 0;
  return items.Select(it => new { Item = it, Index = i++}).First(indexedIt => condition(indexedIt.Item)).Index;
 }

Simple, but the code seems to verbose for that basic operation, so pondering over this for a moment I realized of a shorter way to accomplish this, using TakeWhile and Count:

public static int IndexOfFirst3<T>(this IEnumerable<T> items, Predicate<T> condition)
 {
  return items.TakeWhile(it => !condition(it)).Count();
 }

We would use any of the above extension methods like this:
List<string> cities = new List<string>()
  {
   "Xixon",
   "Berlin",
   "Vienna",
   "Prague"
  };
  Console.WriteLine(cities.IndexOfFirst3(it => it.StartsWith("V")).ToString());  
You can get the code here

No comments:

Post a Comment