75 votes

Vérifier si KeyValuePair existe avec FirstOrDefault de LINQ

J'ai un dictionnaire de type

Dictionary<Guid,int>

Je veux renvoyer la première instance où une condition est remplie en utilisant la méthode suivante

var available = m_AvailableDict.FirstOrDefault(p => p.Value == 0)

Cependant, comment puis-je vérifier que je reçois bien une paire clé-valeur ? Je n'arrive pas à utiliser != ou == pour vérifier par rapport à default(KeyValuePair) sans qu'une erreur de compilation ne se produise. Il existe un fil de discussion similaire aquí qui ne semble pas avoir de solution. Je suis en fait capable de résoudre mon problème particulier en récupérant la clé et en vérifiant la valeur par défaut de Guid, mais je suis curieux de savoir s'il existe un bon moyen de faire cela avec la paire keyvaluepair. Merci

0 votes

J'ai trouvé cette réponse à une autre question a été utile. En gros, utilisez .Where() et ensuite utiliser .Any() sur le résultat pour décider si vous avez obtenu un résultat ou pas.

107voto

Marc Gravell Points 482669

Si vous ne vous souciez que de l'existence, vous pouvez utiliser ContainsValue(0) o Any(p => p.Value == 0) à la place ? Recherche par valeur est inhabituel pour un Dictionary<,> ; si vous faites une recherche par clé, vous pourriez utiliser TryGetValue .

Une autre approche :

var record = data.Where(p => p.Value == 1)
     .Select(p => new { Key = p.Key, Value = p.Value })
     .FirstOrDefault();

Cela renvoie un classe - le sera aussi null si elle n'est pas trouvée.

6 votes

C'est une astuce intéressante qui consiste à utiliser des types anonymes pour se faire passer pour des structures. Il faudra que je m'en souvienne.

1 votes

Non, ce n'est pas ce que je voulais dire. Les tuples sont plus simples, donc cela n'a pas vraiment de sens de penser que les types anonymes "essaient de ressembler à des tuples". L'idée derrière mon commentaire était que si l'on utilise des var vous pouvez remplacer une structure par un type anonyme sans avoir à corriger les références, comme vous l'avez fait ci-dessus.

1 votes

L'approche de la classe anonyme est intéressante, surtout si vous voulez faire quelque chose avec la valeur résultante si elle est trouvée. Cela m'évite d'avoir à taper deux fois le prédicat (une fois pour vérifier .Any, une seconde pour saisir .First). C'est parfait, et c'est exactement ce dont j'avais besoin pour un problème similaire.

26voto

Jon Skeet Points 692016

Je vous suggère de le modifier de cette façon :

var query = m_AvailableDict.Where(p => p.Value == 0).Take(1).ToList();

Vous pouvez alors voir si la liste est vide ou non, et prendre la première valeur si ce n'est pas le cas, par ex.

if (query.Count == 0)
{
    // Take action accordingly
}
else
{
    Guid key = query[0].Key;
    // Use the key
}

Notez qu'il n'y a pas de véritable concept de "première" entrée dans un dictionnaire - l'ordre dans lequel il est itéré n'est pas bien défini. Si vous souhaitez récupérer la paire clé/valeur qui était premièrement entré avec cette valeur, vous aurez besoin d'une sorte de dictionnaire de préservation de l'ordre.

(En supposant que vous souhaitiez réellement connaître la clé - si vous ne recherchez qu'un contrôle d'existence, La solution de Marc est le plus approprié).

19voto

voelklflo Points 21

Utilisez le mot-clé default().

bool exists = !available.Equals(default(KeyValuePair<Guid, int>));

1 votes

Je pense que le résultat devrait être inversé, comme : bool exists = !available.Equals(default(KeyValuePair<Guid, int>)) ;

9voto

Samuel Points 21085

Ce que vous voulez, c'est un Any qui vous donne également l'élément correspondant. Vous pouvez facilement écrire cette méthode vous-même.

public static class IEnumerableExtensions
{
  public static bool TryGetFirst<TSource>(this IEnumerable<TSource> source,
                                          Func<TSource, bool> predicate,
                                          out TSource first)
  {
    foreach (TSource item in source)
    {
      if (predicate(item))
      {
        first = item;
        return true;
      }
    }

    first = default(TSource);
    return false;
  }
}

0 votes

Je pense que je l'appellerais probablement TryGetFirst ou TryFirst, étant donné sa similarité avec TryGetValue/TryParse.

0 votes

TryGetFirst est assez bon. Mais en fin de compte, vous pouvez lui donner le nom que vous voulez.

0 votes

(Mais bonne idée pour une méthode d'extension - +1 :)

2voto

pomarc Points 836

Vous pourriez vérifier si

available.Key==Guid.Empty

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X