¿Encontrar un artículo en la lista por LINQ?

226

Aquí tengo un ejemplo simple para encontrar un elemento en una lista de cadenas. Normalmente lo uso para un bucle o un delegado anónimo para hacerlo así:

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 es nuevo para mí. Tengo curiosidad por saber si puedo usar LINQ para encontrar un elemento en la lista. ¿Cómo es posible?

David.Chu.ca
fuente
Eso es genial. Sin embargo, esos son todos estilo de expresión lamda. Yo uso una lista simple aquí. La lista puede ser una clase con varias propiedades y algunas se utilizan para la búsqueda. Entonces, cualquier forma de LINQ para buscar como "desde ... en ... donde ... seleccione ..."
David.Chu.ca
Nah, lo siento. La mayoría de estos métodos (Primero, Individual, Cualquiera, ...) no se pueden traducir directamente a ese formulario.
R. Martinho Fernandes
No importa, en realidad usted puede deshacerse de las lambdas durante algunos casos ...
R. Martinho Fernandes
Grandes respuestas! Solo quiero tener una idea de la búsqueda LINQ del caso de enumeración.
David.Chu.ca

Respuestas:

478

Hay algunas formas (tenga en cuenta que esta no es una lista completa).

1) Single devolverá un único resultado, pero arrojará una excepción si encuentra ninguno o más de uno (que puede o no ser lo que desea):

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

La nota SingleOrDefault()se comportará igual, excepto que devolverá un valor nulo para los tipos de referencia o el valor predeterminado para los tipos de valor, en lugar de generar una excepción.

2) Dónde devolverá todos los artículos que coincidan con sus criterios, por lo que puede obtener un IEnumerable con un elemento:

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

3) Primero devolverá el primer elemento que coincida con sus criterios:

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

La nota FirstOrDefault()se comportará igual, excepto que devolverá un valor nulo para los tipos de referencia o el valor predeterminado para los tipos de valor, en lugar de generar una excepción.

Rex M
fuente
35
Gran respuesta. Encontré que SingleOrDefault es mi respuesta preferida, igual que Single pero devuelve 'nulo' si no puede encontrarlo.
Eddie Parker
2
No conocía Single () o SingleOrDefault (). Muy útil.
draconis
¿Se pueden usar estos métodos con otras colecciones como ReadOnlyCollectiono ObservableCollection?
Yellavon
@yellavon estos son métodos de extensión en cualquier tipo que implemente IEnumerable<T>oIQueryable<T>
Rex M
44
Una cosa a tener en cuenta sobre el uso de SingleOrDefault es que debido a que arroja una excepción si hay más de una coincidencia en la lista, tiene que recorrer cada elemento, donde FirstOrDefault dejará de buscar una vez que se encuentre la primera coincidencia. msdn.microsoft.com/en-us/library/bb342451(v=vs.110).aspx
DavidWainwright
73

Si desea el índice del elemento, esto lo hará:

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

No puedes deshacerte de la lambda en la primera pasada.

Tenga en cuenta que esto arrojará si el elemento no existe. Esto resuelve el problema recurriendo a entradas anulables:

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

Si quieres el artículo:

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

Si desea contar la cantidad de elementos que coinciden:

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

Si quieres todos los artículos que coinciden:

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

Y no olvide consultar la lista nullen cualquiera de estos casos.

O usar en (list ?? Enumerable.Empty<string>())lugar de list.

Gracias a Pavel por ayudar en los comentarios.

R. Martinho Fernandes
fuente
2
Dos puntos. Primero, no hay necesidad real de usar string.Equalsaquí, no tiene nada de malo ==. En segundo lugar, también mencionaría FirstOrDefault(para los casos en que el elemento podría no estar allí), y Selectcon el índice para cubrir el caso donde se necesita el índice del elemento (como está en la muestra en la pregunta en sí).
Pavel Minaev
No estoy feliz todavía. No hay índice -1 (no encontrado) en mi ejemplo. ¿Cualquier sugerencia?
R. Martinho Fernandes
Además de verificar su existencia con if, primero.
R. Martinho Fernandes
¿Debo verificar si la lista es nula primero?
David.Chu.ca
Seleccione lanza ArgumentNullExceptionsi la fuente es nulo
R. Martinho Fernandes
13

Si realmente List<string>no necesita LINQ, simplemente use:

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

Si está buscando el artículo en sí, intente:

string GetItem(string search)
{
    return _list == null ? null : _list.FirstOrDefault(s => s.Equals(search));
}
AgileJon
fuente
1
Siguiendo la lógica del primer ejemplo, podríamos usar _list.Find(search)para el segundo.
jwg
12

¿Desea el elemento en la lista o el elemento en sí mismo (supondría el elemento en sí).

Aquí hay un montón de opciones para ti:

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
fuente
Whoa ... algunas personas son súper rápido uno el gatillo;)
Kelsey
¿Qué tal el índice como valor de retorno?
David.Chu.ca
¿y necesito verificar si _list es nulo en forma de from .. in _list ...?
David.Chu.ca
6

Este método es más fácil y seguro.

var lOrders = new List<string>();

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

RckLN
fuente
1
Creo que ni siquiera necesitamos a true : falsecontinuación debería funcionar igual bool insertOrderNew = lOrders.Find(r => r == "1234") == null;
Vbp
5

¿Qué tal IndexOf?

Busca el objeto especificado y devuelve el índice de la primera aparición dentro de la lista

Por ejemplo

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

Tenga en cuenta que devuelve -1 si el valor no aparece en la lista

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

Solía ​​usar un diccionario que es una especie de lista indexada que me dará exactamente lo que quiero cuando lo quiero.

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

Cada vez que deseo acceder a mis valores de márgenes, por ejemplo, dirijo mi diccionario:

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

Entonces, dependiendo de lo que esté haciendo, un diccionario puede ser útil.

Will Marcouiller
fuente
Gran respuesta a una pregunta diferente.
jwg
2

Aquí hay una forma de reescribir su método para usar 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;
}

Por lo tanto, llamándolo con

GetItemIndex("two")volverá 1,

y

GetItemIndex("notthere")devolverá -1.

Referencia: linqsamples.com

Brinch
fuente
1

Prueba 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
fuente
1

Si necesitamos encontrar un elemento de la lista, entonces podemos usar el método Findy FindAllextensiones, pero hay una ligera diferencia entre ellos. Aquí hay un ejemplo.

 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
fuente
1

Esto lo ayudará a obtener el primer valor o el valor predeterminado en su búsqueda en la Lista de Linq

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

Esta búsqueda encontrará el primer valor predeterminado que devolverá.

befree2j
fuente
0

Desea buscar un objeto en la lista de objetos.

Esto lo ayudará a obtener el primer valor o el valor predeterminado en su búsqueda en la Lista de Linq.

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

o

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

Puede usar FirstOfDefault con la extensión Where Linq para obtener una clase MessageAction de IEnumerable. Reme

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

dónde

Lista de acciones {get; conjunto; }

Leon de Oro
fuente