175

I wrote this:

public static class EnumerableExtensions
{
    public static int IndexOf<T>(this IEnumerable<T> obj, T value)
    {
        return obj
            .Select((a, i) => (a.Equals(value)) ? i : -1)
            .Max();
    }

    public static int IndexOf<T>(this IEnumerable<T> obj, T value
           , IEqualityComparer<T> comparer)
    {
        return obj
            .Select((a, i) => (comparer.Equals(a, value)) ? i : -1)
            .Max();
    }
}

But I don't know if it already exists, does it?

Jader Dias
  • 88,211
  • 155
  • 421
  • 625
  • 5
    The problem with a `Max` approach is that a: it keeps looking, and b: it returns the **last** index when there are duplicates (people usually expect the first index) – Marc Gravell Aug 17 '09 at 21:46
  • 4
    [geekswithblogs.net](http://geekswithblogs.net/BlackRabbitCoder/archive/2011/01/06/c.net-ndash-finding-an-itemrsquos-index-in-ienumerablelttgt.aspx) compares 4 solutions and their performance. The `ToList()/FindIndex()` trick performs best – nixda Jan 31 '16 at 15:10
  • @nixda That link didn't work. But ToList() doesn't sound like the most efficient solution. The one by Marc Graveli stops when it finds a match. – KevinVictor Mar 04 '21 at 16:33
  • 1
    @KevinVictor You can still have a look at it via [web.archive.org](https://web.archive.org/web/20160424204804/http://geekswithblogs.net/BlackRabbitCoder/archive/2011/01/06/c.net-ndash-finding-an-itemrsquos-index-in-ienumerablelttgt.aspx) – nixda Mar 04 '21 at 22:54
  • Oh, interesting... that would change what the best answer is, if that's really the case. (hoping someone can verify) – KevinVictor Mar 05 '21 at 15:04
  • Maybe it depends on what the underlying object is that implements IEnumerable. – KevinVictor Mar 05 '21 at 15:24

12 Answers12

146

I'd question the wisdom, but perhaps:

source.TakeWhile(x => x != value).Count();

(using EqualityComparer<T>.Default to emulate != if needed) - but you need to watch to return -1 if not found... so perhaps just do it the long way

public static int IndexOf<T>(this IEnumerable<T> source, T value)
{
    int index = 0;
    var comparer = EqualityComparer<T>.Default; // or pass in as a parameter
    foreach (T item in source)
    {
        if (comparer.Equals(item, value)) return index;
        index++;
    }
    return -1;
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 11
    +1 for "questioning the wisdom". 9 times out of 10 it's probably a bad idea in the first place. – Joel Coehoorn Aug 17 '09 at 21:45
  • The explicit loop solution also runs 2x faster (in the worst case) than the Select().Max() solution too. – Steve Guidi Aug 17 '09 at 21:46
  • 1
    You can just Count elements by lambda without TakeWhile - it saves one loop: source.Count(x => x != value); – Kamarey Nov 26 '09 at 15:20
  • 13
    @Kamarey - no, that does something different. TakeWhile **stops** when it gets a failure; Count(predicate) returns the ones that match. i.e. if the first was a miss and everything else was true, TakeWhile(pred).Count() will report 0; Count(pred) will report n-1. – Marc Gravell Nov 26 '09 at 16:27
  • 3
    `TakeWhile` is clever! Bear in mind though this returns `Count` if element doesn't exist which is a deviation from standard behaviour. – nawfal Nov 12 '17 at 11:50
  • The "long way" solution could probably be improved regarding performance with fast paths for sources that can be casted to `IList` or `IReadOnlyList`. – Theodor Zoulias Mar 30 '20 at 22:09
59

The whole point of getting things out as IEnumerable is so you can lazily iterate over the contents. As such, there isn't really a concept of an index. What you are doing really doesn't make a lot of sense for an IEnumerable. If you need something that supports access by index, put it in an actual list or collection.

Scott Dorman
  • 42,236
  • 12
  • 79
  • 110
  • 8
    Currently I came accross this thread because I'm implementing a generic IList<> wrapper for the IEnumerable<> type in order to use my IEnumerable<> objects with third party components which only support datasources of type IList. I agree that trying to get an index of an element within an IEnumerable object is probably in most cases a sign of something beign done wrong there are times when finding such index once beats reproducing a large collection in memory just for the sake of finding the index of a single element when you already have an IEnumerable. – jpierson Nov 11 '09 at 17:53
  • 266
    -1 cause: There are legitimate reasons why you want to get an index out of a `IEnumerable<>`. I don't buy the whole "you shoul'd be doing this" dogma. – John Alexiou Dec 14 '10 at 18:40
  • 94
    Agree with @ja72; if you shouldn't be dealing with indexes with `IEnumerable` then `Enumerable.ElementAt` would not exist. `IndexOf` is simply the inverse -- any argument against it must apply equally to `ElementAt`. – Kirk Woll Sep 03 '11 at 20:45
  • 9
    Cleary, C# misses the concept of IIndexableEnumerable. that would just be the equivalent of an "random accessible" concept, in C++ STL terminology. – v.oddou Mar 28 '13 at 04:02
  • 1
    Suppose you wanted to find the index of a member of [`SortedSet`](http://msdn.microsoft.com/en-us/library/dd412070.aspx), which enforces the uniqueness of each of its members? – DavidRR Dec 18 '13 at 15:08
  • 20
    extensions with overloads like Select((x, i) => ...) seem to imply that these indexes should exist – Michael Mar 19 '14 at 16:05
  • List myList = new List(myIEnumerable); – Rick the Scapegoat Sep 13 '17 at 19:08
  • The whole point of IEnumerable is to not force consumers of objects into a specific camp (List, ObservableCollection, Collection, etc) Allowing them to define method parameters and return types how they see fit, as long as it implements IEnumerable – Rick the Scapegoat Sep 13 '17 at 19:16
  • 5
    For `IndexOf` to be meaningful, the IEnumerable must be ordered (don't confuse with sorted), and must be able to be enumerated more than once. I suspect this method isn't part of LINQ because not all IEnumerables meet those criteria. But, in nearly all cases where you want to use this you know that the the underlying collection DOES meet those criteria, so I wish it were included. – Darryl Oct 27 '17 at 20:31
  • Stumbled upon this while working with OpenXML.. you can only refer to the styles by their Indices and you can only access them via enumerators... Now you would think a good idea would be to be able to stop enumeration if the desired style was found, rather than eagerly build long lists prior to traversal. I am fairly new to this, maybe there is an elegant way to achieve this... – Palcente Feb 26 '18 at 18:56
  • @ja72 What do you make of the comments above stating "For IndexOf to be meaningful, the IEnumerable must be ordered (don't confuse with sorted), and must be able to be enumerated more than once." Don't you think it is a valid argument against getting the index of IEnumerable? – Tarik Jun 03 '19 at 05:26
  • 1
    @Darryl Excellent argument. However, someone pointed out the fact that Enumerable implements ElementAt which assumes some ordering, although Enumerable being a class may specifically have an ordering. What do you think? – Tarik Jun 03 '19 at 05:29
  • 1
    This is hardly an answer... And what should be done if we don't control the `IEnumerable` class? For example, I need to grab an Excel spreadsheet's named ranges by index. Check out [`Microsoft.Office.Interop.Excel` `Names`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.office.interop.excel.names?view=excel-pia). How can I get the nth named range? – ctwheels Oct 07 '20 at 15:10
  • I can agree that using indicies over IEnumerable would be conceptually incorrect, However I feel that this response completely fails to understand that problems has context. I don't think the `ElementAt` method is an excuse for a `IndexOf` method. What really makes sense here is that we wish the get the first item after a certain number of items. (It's not an `ElementAt` an Index, it's a first element after a count) - Likewise - I think it is perfectly reasonable to have a method that phrases "How many items did I get before I got a match" - The name `IndexOf` is simply from familiarity. – Jens Mar 20 '23 at 13:03
  • " If you need something that supports access by index, put it in an actual list or collection", Why would you assume that the item is ours to implement or re-implement? Some people use software and API's they're not allowed to change. Why would anyone pretend this is not well understood. – Josh Sutterfield May 10 '23 at 00:03
32

I would implement it like this:

public static class EnumerableExtensions
{
    public static int IndexOf<T>(this IEnumerable<T> obj, T value)
    {
        return obj.IndexOf(value, null);
    }

    public static int IndexOf<T>(this IEnumerable<T> obj, T value, IEqualityComparer<T> comparer)
    {
        comparer = comparer ?? EqualityComparer<T>.Default;
        var found = obj
            .Select((a, i) => new { a, i })
            .FirstOrDefault(x => comparer.Equals(x.a, value));
        return found == null ? -1 : found.i;
    }
}
dahlbyk
  • 75,175
  • 8
  • 100
  • 122
  • 1
    That's actually very cute, +1! It involves extra objects, but they should be *relatively* cheap (GEN0), so not a huge problem. The `==` might need work? – Marc Gravell Aug 17 '09 at 21:48
  • 1
    Added IEqualityComparer overload, in true LINQ style. ;) – dahlbyk Aug 17 '09 at 21:55
  • 1
    I think you mean to say ... comparer.Equals(x.a, value) =) – Marc Aug 17 '09 at 21:55
  • Since the Select expression is returning the combined result, which is then processed, I'd imagine explicitly using the KeyValuePair value type would allow you to avoid any sort of heap allocations, so long as the .NET impl allocates value types on the stack and any state machine which LINQ may generate uses a field for the Select'd result which isn't declared as a bare Object (thus causing the KVP result to get boxed). Of course, you'd have to rework the found==null condition (since found would now be a KVP value). Maybe using DefaultIfEmpty() or `KVP` (nullable index) – kornman00 Jul 20 '14 at 10:59
  • You're correct that there are extra allocations for the intermediate result. Though if you're that constrained for performance you'll probably avoid using LINQ altogether. :) – dahlbyk Jul 21 '14 at 18:46
  • 1
    Nice implementation, though one thing I'd suggest adding is a check to see if obj implements `IList` and if so, defer to its IndexOf method just in case it has a type-specific optimization. – Josh Sep 17 '15 at 17:34
23

The way I'm currently doing this is a bit shorter than those already suggested and as far as I can tell gives the desired result:

 var index = haystack.ToList().IndexOf(needle);

It's a bit clunky, but it does the job and is fairly concise.

Mark Watts
  • 724
  • 7
  • 15
  • 8
    Though this would work for small collections, suppose you have a million items in the "haystack". Doing a ToList() on that will iterate through all one-million elements and add them to a list. Then it will search through the list to find the matching element's index. This would be extremely inefficient as well as the possibility of throwing an exception if the list gets too big. – esteuart Feb 13 '15 at 17:59
  • 5
    @esteuart Definitely - you need to choose an approach which suits your use case. I doubt there's a one size fits all solution, which is possibly why there isn't an implementation in the core libraries. – Mark Watts Feb 16 '15 at 09:42
  • 1
    @esteuart Hmm... See the link by nixda in the comments under the main questions. My thinking was similar to yours but the analysis shows that ToList() / FindIndex() is more efficient. – KevinVictor Mar 05 '21 at 15:02
  • 1
    @KevinVictor That is an interesting article, though it may not show the whole picture. The `ToList()` method has two branches: One, when the underlying object implements `ICollection` the other when it doesn't. It seems that the algorithm used by the author uses a List for the backing instance. Because `List` implements `ICollection`, it takes the first branch which does a memory copy of the underlying array. This is extremely fast and would account for the results. I'm interested to see a comparison using an `IEnumerable` instance that does not implement ICollection. – esteuart Mar 06 '21 at 17:58
  • 1
    @KevinVictor There is also still the concern of an `OutOfMemoryException` if the source is sufficiently large. – esteuart Mar 06 '21 at 18:00
  • 1
    @esteuart Interesting. My first thought is: what if the underlying object _is_ a List. Wouldn't it be most efficient to first check for that and if so, just do a simple IndexOf; otherwise, first do a ToList. (Similarly if the underlying object is an array.) – KevinVictor Mar 07 '21 at 18:22
  • 1
    @KevinVictor That is a really good point. Maybe the best solution would be to check if the enumerable is a `List` and if so do `IndexOf()` (like you said). If not, check if it implements `ICollection` (which arrays do). If it does, do `ToList().IndexOf()` and if not use an iterative approach to find the index. – esteuart Mar 08 '21 at 20:21
12

I think the best option is to implement like this:

public static int IndexOf<T>(this IEnumerable<T> enumerable, T element, IEqualityComparer<T> comparer = null)
{
    int i = 0;
    comparer = comparer ?? EqualityComparer<T>.Default;
    foreach (var currentElement in enumerable)
    {
        if (comparer.Equals(currentElement, element))
        {
            return i;
        }

        i++;
    }

    return -1;
}

It will also not create the anonymous object

Axente Adrian
  • 129
  • 1
  • 2
8

The best way to catch the position is by FindIndex This function is available only for List<>

Example

int id = listMyObject.FindIndex(x => x.Id == 15); 

If you have enumerator or array use this way

int id = myEnumerator.ToList().FindIndex(x => x.Id == 15); 

or

 int id = myArray.ToList().FindIndex(x => x.Id == 15); 
daniele3004
  • 13,072
  • 12
  • 67
  • 75
5

A bit late in the game, i know... but this is what i recently did. It is slightly different than yours, but allows the programmer to dictate what the equality operation needs to be (predicate). Which i find very useful when dealing with different types, since i then have a generic way of doing it regardless of object type and <T> built in equality operator.

It also has a very very small memory footprint, and is very, very fast/efficient... if you care about that.

At worse, you'll just add this to your list of extensions.

Anyway... here it is.

 public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
 {
     int retval = -1;
     var enumerator = source.GetEnumerator();

     while (enumerator.MoveNext())
     {
         retval += 1;
         if (predicate(enumerator.Current))
         {
             IDisposable disposable = enumerator as System.IDisposable;
             if (disposable != null) disposable.Dispose();
             return retval;
         }
     }
     IDisposable disposable = enumerator as System.IDisposable;
     if (disposable != null) disposable.Dispose();
     return -1;
 }

Hopefully this helps someone.

MaxOvrdrv
  • 1,780
  • 17
  • 32
  • 1
    Maybe I'm missing something, but why the `GetEnumerator` and `MoveNext` rather than just a `foreach`? – Josh Gallagher Jan 11 '15 at 23:30
  • 1
    Short answer? Efficiency. Long answer: http://msdn.microsoft.com/en-us/library/9yb8xew9.aspx – MaxOvrdrv Jan 12 '15 at 23:58
  • 2
    Looking at the IL it appears that the performance difference is that a `foreach` will call `Dispose` on the enumerator if it implements `IDisposable`. (See http://stackoverflow.com/questions/4982396/does-foreach-automatically-call-dispose) As the code in this answer doesn't know if the result of calling `GetEnumerator` is or isn't disposable it should do the same. At that point I'm still unclear that there's a perf benefit, though there was some extra IL whose purpose didn't leap out at me! – Josh Gallagher Jan 13 '15 at 10:52
  • @JoshGallagher I did a bit of research a while back regarding perf benefits between foreach and for(i), and the main benefit of using for(i) was that it ByRefs the object in-place rather than re-creating it/passing it back ByVal. I would assume the same applies to MoveNext versus foreach, but i'm not sure about that one. Maybe they both use ByVal... – MaxOvrdrv Jan 13 '15 at 19:49
  • 2
    Reading this blog (http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx) it may be that the "iterator loop" to which he is referring is a `foreach` loop, in which case for the particular case of `T` being a value type it might be saving a box/unbox operation by using the while loop. However, this isn't borne out by the IL I got from a version of your answer with `foreach`. I do still think the conditional disposal of the iterator is important, though. Could you modify the answer to include that? – Josh Gallagher Jan 14 '15 at 14:46
  • Sorry - So this is a good example of dangerous premature optimizations, without enough understanding and forgetting to benchmark??? o.O... The disposing above is not implemented correctly, when done "correctly" (which is less that the above) performance is for all intends and purposes identical. (Well the foreach loop is essentially lowered to the same code so DUH!...) and they allocate exactly the same. So I would avoid this answer. – Jens Mar 20 '23 at 13:56
  • Now I would give @MaxOvrdrv the benefit of doubt here and say this is a 9 year old response, so there may certainly have been improvements, and my benchmarks has been on .NET 481, .NET 6 and .NET 7. – Jens Mar 20 '23 at 13:57
5

A few years later, but this uses Linq, returns -1 if not found, doesn't create extra objects, and should short-circuit when found [as opposed to iterating over the entire IEnumerable]:

public static int IndexOf<T>(this IEnumerable<T> list, T item)
{
    return list.Select((x, index) => EqualityComparer<T>.Default.Equals(item, x)
                                     ? index
                                     : -1)
               .FirstOr(x => x != -1, -1);
}

Where 'FirstOr' is:

public static T FirstOr<T>(this IEnumerable<T> source, T alternate)
{
    return source.DefaultIfEmpty(alternate)
                 .First();
}

public static T FirstOr<T>(this IEnumerable<T> source, Func<T, bool> predicate, T alternate)
{
    return source.Where(predicate)
                 .FirstOr(alternate);
}
Greg
  • 121
  • 2
  • 7
  • Another way of doing it can be: public static int IndexOf(this IEnumerable list, T item) { int e = list.Select((x, index) => EqualityComparer.Default.Equals(item, x) ? x + 1 : -1) .FirstOrDefault(x => x > 0); return (e == 0) ? -1 : e - 1); } – Anu Thomas Chandy Mar 26 '18 at 23:36
  • "doesn't create extra objects". Linq will in fact create objects in the background so this is not fully correct. Both `source.Where` and `source.DefaultIfEmpty` will for example create an `IEnumerable` each. – Martin Odhelius Aug 22 '19 at 13:14
4

Stumbled across this today in a search for answers and I thought I'd add my version to the list (No pun intended). It utlises the null conditional operator of c#6.0

IEnumerable<Item> collection = GetTheCollection();

var index = collection
.Select((item,idx) => new { Item = item, Index = idx })
//or .FirstOrDefault(_ =>  _.Item.Prop == something)
.FirstOrDefault(_ => _.Item == itemToFind)?.Index ?? -1;

I've done some 'racing of the old horses' (testing) and for large collections (~100,000), worst case scenario that item you want is at the end, this is 2x faster than doing ToList().FindIndex(). If the Item you want is in the middle its ~4x faster.

For smaller collections (~10,000) it seems to be only marginally faster

Heres how I tested it https://gist.github.com/insulind/16310945247fcf13ba186a45734f254e

Dave
  • 2,829
  • 3
  • 17
  • 44
1

An alternative to finding the index after the fact is to wrap the Enumerable, somewhat similar to using the Linq GroupBy() method.

public static class IndexedEnumerable
{
    public static IndexedEnumerable<T> ToIndexed<T>(this IEnumerable<T> items)
    {
        return IndexedEnumerable<T>.Create(items);
    }
}

public class IndexedEnumerable<T> : IEnumerable<IndexedEnumerable<T>.IndexedItem>
{
    private readonly IEnumerable<IndexedItem> _items;

    public IndexedEnumerable(IEnumerable<IndexedItem> items)
    {
        _items = items;
    }

    public class IndexedItem
    {
        public IndexedItem(int index, T value)
        {
            Index = index;
            Value = value;
        }

        public T Value { get; private set; }
        public int Index { get; private set; }
    }

    public static IndexedEnumerable<T> Create(IEnumerable<T> items)
    {
        return new IndexedEnumerable<T>(items.Select((item, index) => new IndexedItem(index, item)));
    }

    public IEnumerator<IndexedItem> GetEnumerator()
    {
        return _items.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Which gives a use case of:

var items = new[] {1, 2, 3};
var indexedItems = items.ToIndexed();
foreach (var item in indexedItems)
{
    Console.WriteLine("items[{0}] = {1}", item.Index, item.Value);
}
Joshka
  • 766
  • 7
  • 16
  • great baseline. It is helpful to add members IsEven, IsOdd, IsFirst and IsLast as well. – JJS May 18 '17 at 20:34
0

This can get really cool with an extension (functioning as a proxy), for example:

collection.SelectWithIndex(); 
// vs. 
collection.Select((item, index) => item);

Which will automagically assign indexes to the collection accessible via this Index property.

Interface:

public interface IIndexable
{
    int Index { get; set; }
}

Custom extension (probably most useful for working with EF and DbContext):

public static class EnumerableXtensions
{
    public static IEnumerable<TModel> SelectWithIndex<TModel>(
        this IEnumerable<TModel> collection) where TModel : class, IIndexable
    {
        return collection.Select((item, index) =>
        {
            item.Index = index;
            return item;
        });
    }
}

public class SomeModelDTO : IIndexable
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }

    public int Index { get; set; }
}

// In a method
var items = from a in db.SomeTable
            where a.Id == someValue
            select new SomeModelDTO
            {
                Id = a.Id,
                Name = a.Name,
                Price = a.Price
            };

return items.SelectWithIndex()
            .OrderBy(m => m.Name)
            .Skip(pageStart)
            .Take(pageSize)
            .ToList();
Yom T.
  • 8,760
  • 2
  • 32
  • 49
-1

Try this:

static int FindIndex<T>(this IEnumerable<T> a, Predicate<T> f) =>
    a.TakeWhile(x => !f(x)).Count();

static int IndexOf<T>(this IEnumerable<T> a, T value) =>
    a.FindIndex(x => EqualityComparer<T>.Default.Equals(x, value));

var i = new[] { 1, 2, 3 }.IndexOf(2); // 1
AFatNiBBa
  • 39
  • 3
  • That only reshuffles a part of what's said in [this answer](https://stackoverflow.com/a/1290638/861716) given in 2009. – Gert Arnold Nov 17 '22 at 13:37