[c#] How to get the index of an item in a list in a single step?

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));

This question is related to c# linq lookup

The answer is


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.


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

int i = Array.FindIndex(yourArray, x => x == itemYouWant);

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.

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;
    }
}

  1. Simple solution to find index for any string value in the List.

Here is code for List Of String:

int indexOfValue = myList.FindIndex(a => a.Contains("insert value from list"));
  1. Simple solution to find index for any Integer value in the List.

Here is Code for List Of Integer:

    int indexOfNumber = myList.IndexOf(/*insert number from list*/);

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 list only once.


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.