¿Cómo obtener el índice de un elemento en una lista en un solo paso?

193

¿Cómo puedo encontrar el índice de un elemento en una lista sin recorrerlo?

Actualmente, esto no se ve muy bien: busca en la lista el mismo artículo dos veces, solo para obtener el índice:

var oProp = something;

int theThingIActuallyAmInterestedIn = myList.IndexOf(myList.Single(i => i.Prop == oProp));
Daniel Robinson
fuente

Respuestas:

434

¿Qué tal el Método List.FindIndex :

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

Este método realiza una búsqueda lineal; por lo tanto, este método es una operación O (n), donde n es Count.

Si no se encuentra el artículo, devolverá -1

Alex Filipovici
fuente
2
¿Qué tal int index?
Dylan Czenski
2
@DylanChensky ha estado codificando demasiado JS
lennyy
9
Como referencia, si el artículo no se encuentra; volverá -1
Daniel Filipe
102

Para tipos simples puede usar "IndexOf":

List<string> arr = new List<string>();
arr.Add("aaa");
arr.Add("bbb");
arr.Add("ccc");
int i = arr.IndexOf("bbb"); // RETURNS 1.
José Manuel Abarca Rodríguez
fuente
71

EDITAR: si solo está utilizando List<>ay solo necesita el índice, entonces List.FindIndexes el mejor enfoque. Dejaré esta respuesta aquí para aquellos que necesitan algo diferente (por ejemplo, encima de cualquiera IEnumerable<>).

Utilice la sobrecarga de los Selectcuales toma un índice en el predicado, por lo que transforma su lista en un par (índice, valor):

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

Luego:

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

O si solo desea el índice y lo está usando en varios lugares, podría escribir fácilmente su propio método de extensión, que en Wherelugar de devolver los elementos originales, devolvió los índices de esos elementos que coincidían con el predicado.

Jon Skeet
fuente
Parece que todo lo que quiere es el índice. List <>. FindIndex (Predicate <>) es el mejor enfoque. Aunque el título de la pregunta insinuaría lo contrario, la descripción del OP es bastante clara: solo necesita el índice "int theThingIActuallyAmInterestedIn"
Louis Ricci el
1
@LastCoder: Aha: se había perdido FindIndex. Sí, estoy completamente de acuerdo.
Jon Skeet
Para ser claros, ¿el "índice / valor -> enfoque único" es "mejor" (aquí significa ser más rápido en términos de Big-O) que iterar manualmente dos veces? ¿O es el proveedor LINQ2Objects lo suficientemente inteligente como para optimizar una de las iteraciones? (Supongo que tanto Select como Single en general son operaciones O (n))
sara
1
@kai: Creo que necesitas leer sobre cómo funciona LINQ, básicamente. Es demasiado complicado explicarlo en detalle en un comentario. Sin embargo ... esto solo está iterando sobre la colección de origen una vez. LINQ configura una tubería, que transforma perezosamente la secuencia de entrada en otra secuencia, y luego la Single()operación itera sobre esa secuencia y encuentra el elemento único que coincide con el predicado. Para más detalles, lea mi serie de blogs edulinq
Jon Skeet
1
+1 Necesitaba esta solución. El jefe pensó que era inteligente por una vez. Me aconsejaron documentar esto cuidadosamente, ya que usaba un tipo anónimo y podría no estar claro para el próximo codificador en el área.
Adam Wells
14

Si no desea usar LINQ, entonces:

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

de esta manera, está iterando la lista solo una vez.

gzaxx
fuente
22
@KingKing nadie dijo que sí.
Tomer W
1
¿Es esta la misma implementación que Linq FindIndexfuera de interés?
Coops
2
probablemente no sea el mismo código, List tiene algunas optimizaciones ordenadas aquí y allá. pero me resulta difícil creer que puedan buscar en una lista desordenada en menos de O (n), por lo que diría que probablemente sean realmente similares en la práctica.
Sara
6
  1. Solución simple para encontrar el índice para cualquier valor de cadena en la Lista.

Aquí está el código para la lista de cadenas:

int indexOfValue = myList.FindIndex(a => a.Contains("insert value from list"));
  1. Solución simple para encontrar el índice de cualquier valor entero en la lista.

Aquí está el código para la lista de enteros:

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

fuente
2

Aquí hay un método de extensión de copiar / pegar para 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;
    }
}

Disfrutar.

Snæbjørn
fuente
2

Si alguien se pregunta por la Arrayversión, es así:

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