Trouver un article dans List by LINQ?

226

Ici, j'ai un exemple simple pour trouver un élément dans une liste de chaînes. Normalement, j'utilise for loop ou un délégué anonyme pour le faire comme ceci:

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 est nouveau pour moi. Je suis curieux de savoir si je peux utiliser LINQ pour trouver un élément dans la liste? Comment si possible?

David.Chu.ca
la source
C'est génial. Cependant, ce sont tous des styles d'expression lamda. J'utilise ici une simple liste. La liste peut être une classe avec plusieurs propriétés et certaines sont utilisées pour la recherche. Donc, n'importe quelle façon LINQ de rechercher comme "de ... dans ... où ... sélectionnez ..."
David.Chu.ca
Non, désolé. La plupart de ces méthodes (First, Single, Any, ...) ne peuvent pas être traduites directement dans ce formulaire.
R. Martinho Fernandes
Peu importe, en fait, vous pouvez vous débarrasser des lambdas pour quelques cas ...
R. Martinho Fernandes
Excellentes réponses! Je veux juste avoir un avant-goût de la recherche LINQ à partir d'un cas d'énumération.
David.Chu.ca

Réponses:

478

Il y a plusieurs façons (notez que ce n'est pas une liste complète).

1) Single renverra un seul résultat, mais lèvera une exception s'il n'en trouve pas ou plus d'un (qui peut ou non être ce que vous voulez):

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

Remarque SingleOrDefault()se comportera de la même manière, sauf qu'il renverra null pour les types de référence, ou la valeur par défaut pour les types de valeur, au lieu de lever une exception.

2) retournera tous les articles qui correspondent à vos critères, vous pouvez donc obtenir un IEnumerable avec un élément:

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

3) First retournera le premier article qui correspond à vos critères:

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

Remarque FirstOrDefault()se comportera de la même manière, sauf qu'il renverra null pour les types de référence, ou la valeur par défaut pour les types de valeur, au lieu de lever une exception.

Rex M
la source
35
Très bonne réponse. J'ai trouvé SingleOrDefault comme étant ma réponse de choix - identique à Single mais retourne 'null' s'il ne le trouve pas.
Eddie Parker
2
Je ne connaissais ni Single () ni SingleOrDefault (). Très utile.
draconis
Ces méthodes peuvent-elles être utilisées avec d'autres collections comme ReadOnlyCollectionou ObservableCollection?
yellavon
@yellavon ce sont des méthodes d'extension sur tout type qui implémente IEnumerable<T>ouIQueryable<T>
Rex M
4
Une chose à noter à propos de l'utilisation de SingleOrDefault est que, car il lève une exception si plusieurs correspondances sont dans la liste, il doit parcourir chaque élément, où FirstOrDefault arrêtera la recherche une fois la première correspondance trouvée. msdn.microsoft.com/en-us/library/bb342451(v=vs.110).aspx
DavidWainwright
73

Si vous voulez l'index de l'élément, cela le fera:

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

Vous ne pouvez pas vous débarrasser de la lambda lors de la première passe.

Notez que cela lancera si l'élément n'existe pas. Cela résout le problème en recourant à des valeurs nulles:

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 vous voulez l'article:

// 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 vous souhaitez compter le nombre d'articles qui correspondent:

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

Si vous voulez que tous les éléments correspondent:

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

Et n'oubliez pas de vérifier la liste pour null dans aucun de ces cas.

Ou utilisez (list ?? Enumerable.Empty<string>())au lieu de list.

Merci à Pavel pour son aide dans les commentaires.

R. Martinho Fernandes
la source
2
Deux points. Tout d'abord, il n'y a pas vraiment besoin d'utiliser string.Equalsici - rien de mal à ==. Deuxièmement, je mentionnerais également FirstOrDefault(pour les cas où l'élément pourrait ne pas être là), et Selectavec l'index pour couvrir le cas où l'index de l'élément est nécessaire (comme c'est le cas dans l'échantillon de la question elle-même).
Pavel Minaev
Je ne suis pas encore content. Il n'y a pas d'index -1 (introuvable) dans mon exemple. Toute suggestion?
R. Martinho Fernandes
En plus de vérifier son existence avec if, d'abord.
R. Martinho Fernandes
Dois-je d'abord vérifier si la liste est nulle?
David.Chu.ca
Sélectionnez les lancers ArgumentNullExceptionsi la source est nulle
R. Martinho Fernandes
13

Si List<string>vous n'avez vraiment pas besoin de LINQ, utilisez simplement:

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

Si vous recherchez l'article lui-même, essayez:

string GetItem(string search)
{
    return _list == null ? null : _list.FirstOrDefault(s => s.Equals(search));
}
AgileJon
la source
1
Suivant la logique du premier exemple, nous pourrions utiliser _list.Find(search)pour le second.
jwg
12

Voulez-vous l'article dans la liste ou l'article lui-même (supposerait l'article lui-même).

Voici un tas d'options pour vous:

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
la source
Whoa ... certaines personnes sont super rapides sur la gâchette;)
Kelsey
que diriez-vous de l'index comme valeur de retour?
David.Chu.ca
et dois-je vérifier si _list est null sous la forme from .. in _list ...?
David.Chu.ca
6

Cette méthode est plus simple et plus sûre

var lOrders = new List<string>();

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

RckLN
la source
1
Je pense que nous n'avons même pas besoin true : falseci-dessous devrait fonctionner de la même façon bool insertOrderNew = lOrders.Find(r => r == "1234") == null;
Vbp
5

Et alors IndexOf?

Recherche l'objet spécifié et renvoie l'index de la première occurrence dans la liste

Par exemple

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

Notez qu'il renvoie -1 si la valeur ne se produit pas dans la liste

> boys.IndexOf("Hermione")  
-1
Colonel Panic
la source
2

J'avais l'habitude d'utiliser un dictionnaire qui est une sorte de liste indexée qui me donnera exactement ce que je veux quand je le veux.

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

Chaque fois que je souhaite accéder à mes valeurs de marges, par exemple, j'adresse mon dictionnaire:

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

Ainsi, selon ce que vous faites, un dictionnaire peut être utile.

Will Marcouiller
la source
Excellente réponse à une question différente.
jwg
2

Voici une façon de réécrire votre méthode pour utiliser 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;
}

Ainsi, l'appeler avec

GetItemIndex("two")reviendra 1,

et

GetItemIndex("notthere")reviendra -1.

Référence: linqsamples.com

brinch
la source
1

Essayez ce code:

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
la source
1

Si nous devons trouver un élément dans la liste, nous pouvons utiliser la méthode Findet les FindAllextensions, mais il y a une légère différence entre eux. Voici un exemple.

 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
la source
1

Cela vous aidera à obtenir la première valeur ou la valeur par défaut dans votre recherche Linq List

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

Cette recherche trouvera la première valeur ou la valeur par défaut qu'elle renverra.

befree2j
la source
0

Vous souhaitez rechercher un objet dans la liste d'objets.

Cela vous aidera à obtenir la première valeur ou la valeur par défaut dans votre recherche Linq List.

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
la source
0

Vous pouvez utiliser FirstOfDefault avec l'extension Where Linq pour obtenir une classe MessageAction à partir de IEnumerable. Reme

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

Liste des actions {get; ensemble; }

Lion d'or
la source