78

I have a dictionary of type

Dictionary<Guid,int>

I want to return the first instance where a condition is met using

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

However, how do I check if I'm actually getting back a KeyValuePair? I can't seem to use != or == to check against default(KeyValuePair) without a compiler error. There is a similar thread here that doesn't quite seem to have a solution. I'm actually able to solve my particular problem by getting the key and checking the default of Guid, but I'm curious if there's a good way of doing this with the keyvaluepair. Thanks

Community
  • 1
  • 1
Steve
  • 11,763
  • 15
  • 70
  • 103
  • I found [this answer to another question](http://stackoverflow.com/a/7153921/945456) was helpful. Basically use `.Where()` and then use `.Any()` on the result of that to decide if you got a result back or not. – Jeff B Apr 26 '17 at 19:15

6 Answers6

114

If you just care about existence, you could use ContainsValue(0) or Any(p => p.Value == 0) instead? Searching by value is unusual for a Dictionary<,>; if you were searching by key, you could use TryGetValue.

One other approach:

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

This returns a class - so will be null if not found.

ahsteele
  • 26,243
  • 28
  • 134
  • 248
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 7
    That's a neat trick using Anonymous Types to masquerade as structs. I'll have to remember that. – Andre Luus Jan 26 '12 at 14:08
  • 1
    No, that's not what I meant. Tuples are simpler, so it doesn't really make sense to think that anonymous types are "trying to look like tuples". The idea behind my comment was that if you use `var`, you can replace a struct with an anonymous type without any reference fixes, as you did above. – Andre Luus Jan 27 '12 at 07:07
  • 1
    The anonymous class approach is nice, especially if you want to do anything with the resulting value if found. Saves me the trouble of having to type the predicate out twice (once to check for .Any, second to grab .First). This is perfect, and exactly what I needed for a similar problem. – Mike Loux Dec 08 '16 at 13:44
  • 1
    Note that you could omit the anonymous type property names as if omitted they will use the assigned property names automatically (which in this case are the same) `p => new { p.Key, p.Value }` – oatsoda Feb 20 '17 at 15:39
27

I suggest you change it in this way:

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

You can then see whether the list is empty or not, and take the first value if it's not, e.g.

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

Note that there's no real concept of a "first" entry in a dictionary - the order in which it's iterated is not well-defined. If you want to get the key/value pair which was first entered with that value, you'll need an order-preserving dictionary of some kind.

(This is assuming you actually want to know the key - if you're just after an existence check, Marc's solution is the most appropriate.)

Joshua Duxbury
  • 4,892
  • 4
  • 32
  • 51
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
21

Use the default() keyword.

bool exists = !available.Equals(default(KeyValuePair<Guid, int>));
Florian Sandro Völkl
  • 1,127
  • 13
  • 17
  • 1
    I think the result should be inverted, as: bool exists = !available.Equals(default(KeyValuePair)); – Jaime Feb 07 '17 at 19:18
10

What you want is an Any method that gives you the matching element as well. You can easily write this method yourself.

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
  • 37,778
  • 11
  • 85
  • 87
2

you could check if

available.Key==Guid.Empty
pomarc
  • 2,194
  • 3
  • 23
  • 30
-1

A way to check against the default value of a struct such as KeyValuePair without specifying the type is to create a new instance using Activator:

if (available.Equals(Activator.CreateInstance(available.GetType())))
{
    Console.WriteLine("Not Found!");
}
kevinpo
  • 1,853
  • 19
  • 20
  • I think this is probably similar to Florian's answer using default() keyword. Your approach may be slower at runtime. – jm. May 01 '17 at 22:30