292

How can I find the index of an item in a list without looping through it?

Currently this doesn't look very nice - searching through the list for the same item twice, just to get the index:

var oProp = something;

int theThingIActuallyAmInterestedIn = myList.IndexOf(myList.Single(i => i.Prop == oProp));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Daniel Robinson
  • 13,806
  • 18
  • 64
  • 112
  • Possible duplicate: *[Find an item in a list by LINQ](https://stackoverflow.com/questions/1175645/find-an-item-in-a-list-by-linq)* – Peter Mortensen Jun 11 '21 at 20:32

9 Answers9

572

How about the List.FindIndex Method:

int index = myList.FindIndex(a => a.Prop == oProp);

This method performs a linear search; therefore, this method is an O(n) operation, where n is Count.

If the item is not found, it will return -1

cuongle
  • 74,024
  • 28
  • 151
  • 206
Alex Filipovici
  • 31,789
  • 6
  • 54
  • 78
137

For simple types you can use "IndexOf":

List<string> arr = new List<string>();
arr.Add("aaa");
arr.Add("bbb");
arr.Add("ccc");
int i = arr.IndexOf("bbb"); // Returns 1.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
94

EDIT: If you're only using a List<> and you only need the index, then List.FindIndex is indeed the best approach. I'll leave this answer here for those who need anything different (e.g. on top of any IEnumerable<>).

Use the overload of Select which takes an index in the predicate, so you transform your list into an (index, value) pair:

var pair = myList.Select((Value, Index) => new { Value, Index })
                 .Single(p => p.Value.Prop == oProp);

Then:

Console.WriteLine("Index:{0}; Value: {1}", pair.Index, pair.Value);

Or if you only want the index and you're using this in multiple places, you could easily write your own extension method which was like Where, but instead of returning the original items, it returned the indexes of those items which matched the predicate.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    It seems like all he wants is the index. List<>.FindIndex(Predicate<>) is the best approach. Though the question title would insinuate otherwise, the OP's description is pretty clear he only needs the index "int theThingIActuallyAmInterestedIn" – Louis Ricci Aug 01 '13 at 13:52
  • 1
    @LastCoder: Aha - had missed FindIndex. Yes, I completely agree. – Jon Skeet Aug 01 '13 at 14:00
  • 1
    Just to be clear, is the "index/value -> single" approach "better" (here meaning being faster in terms of Big-O) than manually iterating twice? Or is the LINQ2Objects provider smart enough to optimize away one of the iterations? (I'm making the assumption that both Select and Single generally speaking are O(n) operations) – sara Mar 03 '16 at 07:24
  • 1
    @kai: I think you need to read up on how LINQ works, basically. It's too complicated to explain in detail in a comment. However... this is only iterating over the source collection once. LINQ sets up a pipeline, which lazily transforms the input sequence into another sequence, and then the `Single()` operation iterates over that sequence and finds the single item which matches the predicate. For more details, read my edulinq blog series: http://codeblog.jonskeet.uk/category/edulinq/ – Jon Skeet Mar 03 '16 at 07:38
  • 1
    +1 I needed this solution. Boss thought I was smart for once. I was advised to carefully document this, since it used an anonymous type and may not be clear to the next coder in the area. – Adam Wells Jul 23 '18 at 17:33
17

If you don't want to use LINQ, then:

int index;
for (int i = 0; i < myList.Count; i++)
{
    if (myList[i].Prop == oProp)
    {
       index = i;
       break;
    }
}

This way you are iterating the list only once.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
gzaxx
  • 17,312
  • 2
  • 36
  • 54
  • 22
    @KingKing noone said it is. – Tomer W Aug 01 '13 at 14:01
  • 1
    Is this the same implementation as Linq `FindIndex` out of interest? – Paul C Feb 02 '16 at 12:31
  • 2
    probably not the same code, List has some neat optimizations here and there. but I find it hard to believe they can search an unordered list in less than O(n), so I'd say they're probably really similar in practice. – sara Mar 03 '16 at 07:25
11
  1. A simple solution to find the index for any string value in the List.

    Here is code for a list of strings:

     int indexOfValue = myList.FindIndex(a => a.Contains("insert value from list"));
    
  2. A simple solution to find the index for any integer value in the List.

    Here is code for a list of integers:

     int indexOfNumber = myList.IndexOf(/* insert number from list */);
    
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
5

If anyone wonders for the Array version, it goes like this:

int i = Array.FindIndex(yourArray, x => x == itemYouWant);
Ali Bordbar
  • 128
  • 2
  • 10
3

Here's a copy/paste-able extension method for IEnumerable

public static class EnumerableExtensions
{
    /// <summary>
    /// Searches for an element that matches the conditions defined by the specified predicate,
    /// and returns the zero-based index of the first occurrence within the entire <see cref="IEnumerable{T}"/>.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="list">The list.</param>
    /// <param name="predicate">The predicate.</param>
    /// <returns>
    /// The zero-based index of the first occurrence of an element that matches the conditions defined by <paramref name="predicate"/>, if found; otherwise it'll throw.
    /// </returns>
    public static int FindIndex<T>(this IEnumerable<T> list, Func<T, bool> predicate)
    {
        var idx = list.Select((value, index) => new {value, index}).Where(x => predicate(x.value)).Select(x => x.index).First();
        return idx;
    }
}

Enjoy.

Snæbjørn
  • 10,322
  • 14
  • 65
  • 124
0

That's all fine and good -- but what if you want to select an existing element as the default? In my issue there is no "--select a value--" option.

Here's my code -- you could make it into a one liner if you didn't want to check for no results I suppose...

private void LoadCombo(ComboBox cb, string itemType, string defVal = "")
{
    cb.DisplayMember = "Name";
    cb.ValueMember = "ItemCode";
    cb.DataSource = db.Items.Where(q => q.ItemTypeId == itemType).ToList();

    if (!string.IsNullOrEmpty(defVal))
    {
        var i = ((List<GCC_Pricing.Models.Item>)cb.DataSource).FindIndex(q => q.ItemCode == defVal);
        if (i>=0) cb.SelectedIndex = i;
    }
}
D. Kermott
  • 1,613
  • 17
  • 24
0

And IndexOf can be used directly with the object without an explicit comparison.

List<YourObject> listObj = new List<YourObject>();
    var obj = new YourObject();
    int idx = listObj.IndexOf(obj);
  • Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? **If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient.** Can you kindly [edit] your answer to offer an explanation? – Jeremy Caney Jul 13 '23 at 03:36