Como obter o índice de um item em uma lista em uma única etapa?

193

Como posso encontrar o índice de um item em uma lista sem percorrê-lo?

Atualmente, isso não parece muito bom - pesquisar na mesma lista o mesmo item duas vezes, apenas para obter o índice:

var oProp = something;

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

Respostas:

434

Que tal o método List.FindIndex :

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

Este método realiza uma pesquisa linear; portanto, esse método é uma operação O (n), onde n é Count.

Se o item não for encontrado, ele retornará -1

Alex Filipovici
fonte
2
Que tal int index?
Dylan Czenski
2
@DylanChensky ele tem sido codificação JS demais
lennyy
9
Para referência, se o item não for encontrado; retornará -1
Daniel Filipe
102

Para tipos simples, você pode usar "IndexOf":

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

Edição: Se você estiver usando apenas um List<>e você precisa do índice, então List.FindIndexé realmente a melhor abordagem. Deixarei esta resposta aqui para aqueles que precisam de algo diferente (por exemplo, além de qualquer um IEnumerable<>).

Use a sobrecarga Selectque requer um índice no predicado, para transformar sua lista em um par (índice, valor):

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

Então:

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

Ou, se você deseja apenas o índice e o usa em vários lugares, pode escrever facilmente seu próprio método de extensão Where, mas, em vez de retornar os itens originais, ele retornou os índices dos itens correspondentes ao predicado.

Jon Skeet
fonte
Parece que tudo o que ele quer é o índice. Lista <>. FindIndex (Predicado <>) é a melhor abordagem. Embora o título da pergunta seria insinuar o contrário, a descrição do OP é bastante claro que ele só precisa do "int theThingIActuallyAmInterestedIn" index
Louis Ricci
1
@ LastCoder: Aha - tinha perdido o FindIndex. Sim, concordo plenamente.
Jon Skeet
Só para esclarecer, a abordagem "índice / valor -> único" é melhor "(aqui significa ser mais rápida em termos de Big-O) do que a iteração manual duas vezes? Ou o provedor LINQ2Objects é inteligente o suficiente para otimizar uma das iterações? (Eu estou fazendo a suposição de que tanto Select e Single em geral são) as operações n O ()
sara
1
@kai: Eu acho que você precisa ler basicamente como o LINQ funciona. É muito complicado explicar em detalhes em um comentário. No entanto ... isso está apenas repetindo a coleção de origem uma vez. O LINQ configura um pipeline, que transforma lentamente a sequência de entrada em outra sequência e, em seguida, a Single()operação itera sobre essa sequência e localiza o item único que corresponde ao predicado. Para mais detalhes, leia meus edulinq Série Blog: codeblog.jonskeet.uk/category/edulinq
Jon Skeet
1
+1 eu precisava dessa solução. Chefe pensou que eu era inteligente pela primeira vez. Fui aconselhado a documentar isso com cuidado, pois ele usava um tipo anônimo e pode não estar claro para o próximo codificador na área.
Adam Wells
14

Se você não quiser usar o LINQ, então:

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

Dessa forma, você está iterando a lista apenas uma vez.

gzaxx
fonte
22
@KingKing ninguém disse que é.
Tom13 W
1
Essa é a mesma implementação que o Linq FindIndexnão interessa?
Coops 02/02
2
provavelmente não é o mesmo código, a Lista tem algumas otimizações legais aqui e ali. mas acho difícil acreditar que eles possam pesquisar uma lista não ordenada em menos de O (n), então eu diria que provavelmente são realmente semelhantes na prática.
sara
6
  1. Solução simples para encontrar índice para qualquer valor de string na Lista.

Aqui está o código para a lista de seqüências de caracteres:

int indexOfValue = myList.FindIndex(a => a.Contains("insert value from list"));
  1. Solução simples para encontrar índice para qualquer valor Inteiro na Lista.

Aqui está o código da lista de números inteiros:

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

fonte
2

Aqui está um método de extensão capaz de copiar / colar 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;
    }
}

Aproveitar.

Snæbjørn
fonte
2

Se alguém se perguntar pela Arrayversão, ela será assim:

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