Überprüfen Sie, ob KeyValuePair mit FirstOrDefault von LINQ vorhanden ist

75

Ich habe ein Wörterbuch vom Typ

Dictionary<Guid,int>

Ich möchte die erste Instanz zurückgeben, in der eine Bedingung erfüllt ist

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

Wie überprüfe ich jedoch, ob ich tatsächlich ein KeyValuePair zurückerhalte? Ich kann anscheinend nicht! = Oder == verwenden, um ohne einen Compilerfehler gegen die Standardeinstellung (KeyValuePair) zu prüfen. Es gibt einen ähnlichen Thread hier , die nicht ganz scheint eine Lösung zu haben. Ich bin tatsächlich in der Lage, mein spezielles Problem zu lösen, indem ich den Schlüssel erhalte und die Standardeinstellung von Guid überprüfe, aber ich bin gespannt, ob es eine gute Möglichkeit gibt, dies mit dem Schlüsselwertpaar zu tun. Vielen Dank

Steve
quelle
Ich fand diese Antwort auf eine andere Frage hilfreich. Verwenden Sie im Grunde genommen das Ergebnis .Where()und verwenden Sie es dann .Any(), um zu entscheiden, ob Sie ein Ergebnis zurückbekommen haben oder nicht.
Jeff B

Antworten:

107

Wenn Sie sich nur um die Existenz kümmern, könnten Sie ContainsValue(0)oder Any(p => p.Value == 0)stattdessen verwenden? Die Suche nach Wert ist ungewöhnlich für a Dictionary<,>; Wenn Sie nach Schlüssel suchen, können Sie verwenden TryGetValue.

Ein anderer Ansatz:

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

Dies gibt eine Klasse zurück - so wird es sein, nullwenn es nicht gefunden wird.

Marc Gravell
quelle
6
Das ist ein guter Trick, bei dem anonyme Typen als Strukturen maskiert werden. Daran muss ich mich erinnern.
Andre Luus
1
Nein, das habe ich nicht gemeint. Tupel sind einfacher, daher ist es nicht wirklich sinnvoll zu glauben, dass anonyme Typen "versuchen, wie Tupel auszusehen". Die Idee hinter meinem Kommentar war, dass Sie bei Verwendung vareine Struktur durch einen anonymen Typ ohne Referenzkorrekturen ersetzen können, wie Sie es oben getan haben.
Andre Luus
1
Der anonyme Klassenansatz ist hilfreich, insbesondere wenn Sie mit dem resultierenden Wert etwas tun möchten, wenn er gefunden wird. Spart mir die Mühe, das Prädikat zweimal eingeben zu müssen (einmal, um nach .Any zu suchen, zweitens, um .First zu greifen). Das ist perfekt und genau das, was ich für ein ähnliches Problem brauchte.
Mike Loux
1
Beachten Sie, dass Sie die anonymen Typ-Eigenschaftsnamen weglassen können, als würden sie die zugewiesenen Eigenschaftsnamen automatisch verwenden (die in diesem Fall identisch sind)p => new { p.Key, p.Value }
oatsoda
26

Ich schlage vor, Sie ändern es auf folgende Weise:

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

Sie können dann sehen, ob die Liste leer ist oder nicht, und den ersten Wert annehmen, wenn dies nicht der Fall ist, z

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

Beachten Sie, dass es kein wirkliches Konzept für einen "ersten" Eintrag in einem Wörterbuch gibt - die Reihenfolge, in der er iteriert wird, ist nicht genau definiert. Wenn Sie das Schlüssel / Wert-Paar erhalten möchten, das zuerst mit diesem Wert eingegeben wurde, benötigen Sie ein auftragserhaltendes Wörterbuch.

(Dies setzt voraus, dass Sie den Schlüssel tatsächlich wissen möchten. Wenn Sie sich gerade nach einer Existenzprüfung befinden, ist Marc's Lösung am besten geeignet.)

Jon Skeet
quelle
19

Verwenden Sie das Schlüsselwort default ().

bool exists = !available.Equals(default(KeyValuePair<Guid, int>));
Florian Sandro Völkl
quelle
1
Ich denke, das Ergebnis sollte invertiert werden, da: bool existiert =! Available.Equals (Standard (KeyValuePair <Guid, int>));
Jaime
9

Was Sie wollen, ist eine AnyMethode, die Ihnen auch das passende Element gibt. Sie können diese Methode einfach selbst schreiben.

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;
  }
}
Samuel
quelle
Ich denke, ich würde dies wahrscheinlich TryGetFirst oder TryFirst nennen, da es TryGetValue / TryParse ähnelt.
Jon Skeet
TryGetFirst ist ziemlich gut. Aber letztendlich können Sie es benennen, was Sie wollen.
Samuel
(Aber schöne Idee für eine Erweiterungsmethode - +1 :)
Jon Skeet
2

Sie könnten überprüfen, ob

available.Key==Guid.Empty
Pomarc
quelle
-1

Eine Möglichkeit, den Standardwert einer Struktur wie KeyValuePair zu überprüfen, ohne den Typ anzugeben, besteht darin, mit Activator eine neue Instanz zu erstellen:

if (available.Equals(Activator.CreateInstance(available.GetType())))
{
    Console.WriteLine("Not Found!");
}
Kevinpo
quelle
Ich denke, dies ähnelt wahrscheinlich Florians Antwort mit dem Schlüsselwort default (). Ihr Ansatz ist zur Laufzeit möglicherweise langsamer.
jm.