Dictionary.FirstOrDefault() how to determine if a result was found

C#.NetLinq

C# Problem Overview


I have (or wanted to have) some code like this:

IDictionary<string,int> dict = new Dictionary<string,int>();
// ... Add some stuff to the dictionary.

// Try to find an entry by value (if multiple, don't care which one).
var entry = dict.FirstOrDefault(e => e.Value == 1);
if ( entry != null ) { 
   // ^^^ above gives a compile error:
   // Operator '!=' cannot be applied to operands of type 'System.Collections.Generic.KeyValuePair<string,int>' and '<null>'
}

I also tried changing the offending line like this:

if ( entry != default(KeyValuePair<string,int>) ) 

But that also gives a compile error:

Operator '!=' cannot be applied to operands of type 'System.Collections.Generic.KeyValuePair<string,int>' and 'System.Collections.Generic.KeyValuePair<string,int>'

What gives here?

C# Solutions


Solution 1 - C#

Jon's answer will work with Dictionary<string, int>, as that can't have a null key value in the dictionary. It wouldn't work with Dictionary<int, string>, however, as that doesn't represent a null key value... the "failure" mode would end up with a key of 0.

Two options:

Write a TryFirstOrDefault method, like this:

public static bool TryFirstOrDefault<T>(this IEnumerable<T> source, out T value)
{
    value = default(T);
    using (var iterator = source.GetEnumerator())
    {
        if (iterator.MoveNext())
        {
            value = iterator.Current;
            return true;
        }
        return false;
    }
}

Alternatively, project to a nullable type:

var entry = dict.Where(e => e.Value == 1)
                .Select(e => (KeyValuePair<string,int>?) e)
                .FirstOrDefault();

if (entry != null)
{
    // Use entry.Value, which is the KeyValuePair<string,int>
}

Solution 2 - C#

Do it this way:

if ( entry.Key != null )

The thing is that the FirstOrDefault method returns a KeyValuePair<string, int> which is a value type, so it cannot ever be null. You have to determine if a value was found by checking if at least one of its Key, Value properties has its default value. Key is of type string, so checking that for null makes sense considering that the dictionary could not have an item with a null key.

Other approaches you could use:

var entry = dict.Where(e => e.Value == 1)
                .Select(p => (int?)p.Value)
                .FirstOrDefault();

This projects the results into a collection of nullable ints, and if that is empty (no results) you get a null -- there's no way you can mistake that for the int that a successful search would yield.

Solution 3 - C#

Regardless of the types of Key and Value, you could do something like this:

    static void Main(string[] args)
    {
        var dict = new Dictionary<int, string>
        {
            {3, "ABC"},
            {7, "HHDHHGKD"}
        };

        bool found = false;
        var entry = dict.FirstOrDefault(d => d.Key == 3 && (found=true));
        if (found)
        {
            Console.WriteLine("found: " + entry.Value);
        }
        else
        {
            Console.WriteLine("not found");
        }
        Console.ReadLine();
    }

Solution 4 - C#

The clearest code I think is this:

if (dict.ContainsValue(value))
  string key = dict.First(item => item.Value == value).Key;
else
  // do somehing else

Though from the standpoint of speed it's not nice, but there is no better solution. This means that the dictionary will be searched with a slow search a second time. The Dictionary class should be improved by offering a method 'bool TryGetKey(value)'. It looks a little bit strange - because a dictionary is thought to be used in the other direction - but sometimes it's unavoidable to translate backwards.

Solution 5 - C#

for nullable value types, just check.

if ( entry.Value != null ) { 
     //do stuff
} 

for non-nullable types check againts default value, for int, 0.

if ( entry.Value != 0) { 
   //do stuff
} 

Solution 6 - C#

To be fair, casting the object or using a select statement is unnecessary, I wouldn't rely on a try catch to fix the issue either.

Since you're using Linq anyway, what's wrong with using .Any?

var entry;
if (dict.Any(e => e.Value == 1))
{
    // Entry was found, continue work...
    entry = dict.FirstOrDefault(e => e.Value == 1);
}
else
{
    // Entry was not found.
    entry = -1;
}

Obviously, play with it to fit your solution, but it's a fairly quick check that stops if it finds an item in the collection with that value. So it won't check all values if a match has been found.

MSDN Documentation: https://msdn.microsoft.com/en-us/library/bb534972(v=vs.110).aspx

Solution 7 - C#

public static TValue FirstOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, Func<KeyValuePair<TKey, TValue>, bool> where)
    {
        foreach (var kv in dictionary)
        {
            if (where(kv))
                return kv.Value;
        }
        return default;
    }

Solution 8 - C#

Linq FirstOrDefault applying to a Dictionary returns not nullable keyValuePair object anyway. The right way is to check result key or value that it doesn't equal default type value

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEric PetroeljeView Question on Stackoverflow
Solution 1 - C#Jon SkeetView Answer on Stackoverflow
Solution 2 - C#JonView Answer on Stackoverflow
Solution 3 - C#Emerson CardosoView Answer on Stackoverflow
Solution 4 - C#AndresRohrAtlasInformatikView Answer on Stackoverflow
Solution 5 - C#Yusuf DemiragView Answer on Stackoverflow
Solution 6 - C#Luke AldertonView Answer on Stackoverflow
Solution 7 - C#Smagin AlexeyView Answer on Stackoverflow
Solution 8 - C#Vlad HronaView Answer on Stackoverflow