Encontre um item na Lista por LINQ?

226

Aqui eu tenho um exemplo simples para encontrar um item em uma lista de strings. Normalmente eu uso for loop ou delegado anônimo para fazer o seguinte:

int GetItemIndex(string search)
{
   int found = -1;
   if ( _list != null )
   {
     foreach (string item in _list) // _list is an instance of List<string>
     { 
        found++;
        if ( string.Equals(search, item) )
        {
           break;
        }
      }
      /* use anonymous delegate
      string foundItem = _list.Find( delegate(string item) {
         found++;
         return string.Equals(search, item);
      });
      */
   }
   return found;
}

LINQ é novo para mim. Estou curioso para saber se posso usar o LINQ para encontrar itens na lista? Como se possível?

David.Chu.ca
fonte
Isso é ótimo. No entanto, esses são todos os estilos de expressão lamda. Eu uso uma lista simples aqui. A lista pode ser uma classe com várias propriedades e algumas são usadas para pesquisa. Assim, qualquer maneira LINQ para procurar como "de .. em ... onde ... seleccionar ..."
David.Chu.ca
Não, desculpe. A maioria desses métodos (Primeiro, Único, Qualquer ...) não pode ser traduzida diretamente para esse formulário.
R. Martinho Fernandes
Não importa, na verdade, você pode se livrar dos lambdas por alguns casos ...
R. Martinho Fernandes
Ótimas respostas! Eu só quero ter uma amostra da pesquisa LINQ no caso de enumeração.
24920 David.Chu.ca

Respostas:

478

Existem algumas maneiras (observe que essa não é uma lista completa).

1) Single retornará um único resultado, mas lançará uma exceção se encontrar nenhum ou mais de um (que pode ou não ser o que você deseja):

string search = "lookforme";
List<string> myList = new List<string>();
string result = myList.Single(s => s == search);

A nota SingleOrDefault()se comportará da mesma forma, exceto que retornará nulo para tipos de referência ou o valor padrão para tipos de valores, em vez de lançar uma exceção.

2) Onde retornará todos os itens que correspondem aos seus critérios, para que você possa obter um IEnumerable com um elemento:

IEnumerable<string> results = myList.Where(s => s == search);

3) Primeiro retornará o primeiro item que corresponde aos seus critérios:

string result = myList.First(s => s == search);

A nota FirstOrDefault()se comportará da mesma forma, exceto que retornará nulo para tipos de referência ou o valor padrão para tipos de valores, em vez de lançar uma exceção.

Rex M
fonte
35
Ótima resposta. Eu achei SingleOrDefault a minha resposta de escolha - o mesmo que Single, mas retorna 'null' se não conseguir encontrá-lo.
Eddie Parker
2
Eu não conhecia Single () ou SingleOrDefault (). Muito útil.
Draconis
Esses métodos podem ser usados ​​com outras coleções também ReadOnlyCollectionou ObservableCollection?
yellavon
@yellavon estes são métodos de extensão em qualquer tipo que implemente IEnumerable<T>ouIQueryable<T>
Rex M
4
Uma coisa a ser observada sobre o uso do SingleOrDefault é que, porque gera uma exceção se houver mais de uma correspondência na lista, ele precisa percorrer todos os itens, nos quais o FirstOrDefault interromperá a pesquisa assim que a primeira correspondência for encontrada. msdn.microsoft.com/en-us/library/bb342451(v=vs.110).aspx
DavidWainwright
73

Se você deseja o índice do elemento, isso será feito:

int index = list.Select((item, i) => new { Item = item, Index = i })
                .First(x => x.Item == search).Index;

// or
var tagged = list.Select((item, i) => new { Item = item, Index = i });
int index = (from pair in tagged
            where pair.Item == search
            select pair.Index).First();

Você não pode se livrar do lambda no primeiro passe.

Observe que isso será lançado se o item não existir. Isso resolve o problema recorrendo a ints anuláveis:

var tagged = list.Select((item, i) => new { Item = item, Index = (int?)i });
int? index = (from pair in tagged
            where pair.Item == search
            select pair.Index).FirstOrDefault();

Se você deseja o item:

// Throws if not found
var item = list.First(item => item == search);
// or
var item = (from item in list
            where item == search
            select item).First();

// Null if not found
var item = list.FirstOrDefault(item => item == search);
// or
var item = (from item in list
            where item == search
            select item).FirstOrDefault();

Se você deseja contar o número de itens que correspondem:

int count = list.Count(item => item == search);
// or
int count = (from item in list
            where item == search
            select item).Count();

Se você deseja todos os itens que correspondem:

var items = list.Where(item => item == search);
// or
var items = from item in list
            where item == search
            select item;

E não se esqueça de verificar a lista nullem qualquer um desses casos.

Ou use em (list ?? Enumerable.Empty<string>())vez de list.

Obrigado a Pavel por ajudar nos comentários.

R. Martinho Fernandes
fonte
2
Dois pontos. Primeiro, não há necessidade real de usar string.Equalsaqui - nada de errado ==. Segundo, eu também mencionaria FirstOrDefault(nos casos em que o item pode não estar lá), e Selectcom o índice para cobrir o caso em que o índice do item é necessário (como é o exemplo da própria pergunta).
Pavel Minaev 24/07/2009
Ainda não estou feliz. Não há nenhum índice -1 (não encontrado) no meu exemplo. Alguma sugestão?
R. Martinho Fernandes
Além de verificar sua existência com if, primeiro.
R. Martinho Fernandes
Preciso verificar se a lista é nula primeiro?
24920 David.Chu.ca
Selecione arremessos ArgumentNullExceptionse a fonte for nula
R. Martinho Fernandes
13

Se realmente for um, List<string>você não precisa de LINQ, basta usar:

int GetItemIndex(string search)
{
    return _list == null ? -1 : _list.IndexOf(search);
}

Se você estiver procurando pelo item em si, tente:

string GetItem(string search)
{
    return _list == null ? null : _list.FirstOrDefault(s => s.Equals(search));
}
AgileJon
fonte
1
Seguindo a lógica do primeiro exemplo, poderíamos usar _list.Find(search)o segundo.
jwg 24/09
12

Deseja o item na lista ou o próprio item real (assumiria o próprio item).

Aqui estão algumas opções para você:

string result = _list.First(s => s == search);

string result = (from s in _list
                 where s == search
                 select s).Single();

string result = _list.Find(search);

int result = _list.IndexOf(search);
Kelsey
fonte
Whoa ... algumas pessoas são super rápido um gatilho;)
Kelsey
e quanto ao índice como valor de retorno?
David.Chu.ca
e preciso verificar se _list é nulo na forma de from .. in _list ...?
24920 David.Chu.ca
6

Este método é mais fácil e seguro

var lOrders = new List<string>();

bool insertOrderNew = lOrders.Find(r => r == "1234") == null ? true : false

RckLN
fonte
1
Acho que não precisa mesmo true : falseabaixo deve funcionar mesmo bool insertOrderNew = lOrders.Find(r => r == "1234") == null;
VBP
5

Que tal IndexOf?

Procura o objeto especificado e retorna o índice da primeira ocorrência na lista

Por exemplo

> var boys = new List<string>{"Harry", "Ron", "Neville"};  
> boys.IndexOf("Neville")  
2
> boys[2] == "Neville"
True

Observe que ele retorna -1 se o valor não ocorrer na lista

> boys.IndexOf("Hermione")  
-1
Coronel Panic
fonte
2

Eu costumava usar um dicionário que é algum tipo de lista indexada que me dá exatamente o que eu quero quando quero.

Dictionary<string, int> margins = new Dictionary<string, int>();
margins.Add("left", 10);
margins.Add("right", 10);
margins.Add("top", 20);
margins.Add("bottom", 30);

Sempre que desejo acessar meus valores de margens, por exemplo, dirijo-me ao meu dicionário:

int xStartPos = margins["left"];
int xLimitPos = margins["right"];
int yStartPos = margins["top"];
int yLimitPos = margins["bottom"];

Portanto, dependendo do que você está fazendo, um dicionário pode ser útil.

Will Marcouiller
fonte
Ótima resposta para uma pergunta diferente.
jwg 24/09
2

Aqui está uma maneira de reescrever seu método para usar o LINQ:

public static int GetItemIndex(string search)
{
    List<string> _list = new List<string>() { "one", "two", "three" };

    var result = _list.Select((Value, Index) => new { Value, Index })
            .SingleOrDefault(l => l.Value == search);

    return result == null ? -1 : result.Index;
}

Assim, chamando-o com

GetItemIndex("two") retornará 1 ,

e

GetItemIndex("notthere") retornará -1 .

Referência: linqsamples.com

brinch
fonte
1

Tente este código:

return context.EntitytableName.AsEnumerable().Find(p => p.LoginID.Equals(loginID) && p.Password.Equals(password)).Select(p => new ModelTableName{ FirstName = p.FirstName, UserID = p.UserID });
Nayeem Mansoori
fonte
1

Se precisarmos encontrar um elemento da lista, podemos usar o método Finde FindAllextensions, mas há uma pequena diferença entre eles. Aqui está um exemplo.

 List<int> items = new List<int>() { 10, 9, 8, 4, 8, 7, 8 };

  // It will return only one 8 as Find returns only the first occurrence of matched elements.
     var result = items.Find(ls => ls == 8);      
 // this will returns three {8,8,8} as FindAll returns all the matched elements.
      var result1 = items.FindAll(ls => ls == 8); 
Sheo Dayal Singh
fonte
1

Isso o ajudará a obter o primeiro valor ou o valor padrão em sua pesquisa na lista Linq

var results = _List.Where(item => item == search).FirstOrDefault();

Esta pesquisa encontrará o primeiro valor ou valor padrão que ele retornará.

befree2j
fonte
0

Você deseja pesquisar um objeto na lista de objetos.

Isso ajudará você a obter o primeiro valor ou o valor padrão na sua pesquisa da lista Linq.

var item = list.FirstOrDefault(items =>  items.Reference == ent.BackToBackExternalReferenceId);

ou

var item = (from items in list
    where items.Reference == ent.BackToBackExternalReferenceId
    select items).FirstOrDefault();
Huseyin Durmus
fonte
0

Você pode usar FirstOfDefault com a extensão Where Linq para obter uma classe MessageAction do IEnumerable. Reme

var action = Message.Actions.Where (e => e.targetByName == className) .FirstOrDefault ();

Onde

Listar ações {get; conjunto; }

Leão dourado
fonte