Check if Key Exists in NameValueCollection

Is there a quick and simple way to check if a key exists in a NameValueCollection without looping through it?

Looking for something like Dictionary.ContainsKey() or similar.

There are many ways to solve this of course. Just wondering if someone can help scratch my brain itch.


Solution 1:

From MSDN:

This property returns null in the following cases:

1) if the specified key is not found;

So you can just:

NameValueCollection collection = ...
string value = collection[key];
if (value == null) // key doesn't exist

2) if the specified key is found and its associated value is null.

collection[key] calls base.Get() then base.FindEntry() which internally uses Hashtable with performance O(1).

Solution 2:

Use this method:

private static bool ContainsKey(this NameValueCollection collection, string key)
{
    if (collection.Get(key) == null)
    {
        return collection.AllKeys.Contains(key);
    }

    return true;
}

Solution 3:

Yes, you can use Linq to check the AllKeys property:

using System.Linq;
...
collection.AllKeys.Contains(key);

However a Dictionary<string, string[]> would be far more suited to this purpose, perhaps created via an extension method:

public static void Dictionary<string, string[]> ToDictionary(this NameValueCollection collection) 
{
    return collection.Cast<string>().ToDictionary(key => key, key => collection.GetValues(key));
}

var dictionary = collection.ToDictionary();
if (dictionary.ContainsKey(key))
{
   ...
}

Solution 4:

I don't think any of these answers are quite right/optimal. NameValueCollection not only doesn't distinguish between null values and missing values, it's also case-insensitive with regards to it's keys. Thus, I think a full solution would be:

public static bool ContainsKey(this NameValueCollection @this, string key)
{
    return @this.Get(key) != null 
        // I'm using Keys instead of AllKeys because AllKeys, being a mutable array,
        // can get out-of-sync if mutated (it weirdly re-syncs when you modify the collection).
        // I'm also not 100% sure that OrdinalIgnoreCase is the right comparer to use here.
        // The MSDN docs only say that the "default" case-insensitive comparer is used
        // but it could be current culture or invariant culture
        || @this.Keys.Cast<string>().Contains(key, StringComparer.OrdinalIgnoreCase);
}

Solution 5:

queryItems.AllKeys.Contains(key)

Be aware that key may not be unique and that the comparison is usually case sensitive. If you want to just get the value of the first matching key and not bothered about case then use this:

        public string GetQueryValue(string queryKey)
        {
            foreach (string key in QueryItems)
            {
                if(queryKey.Equals(key, StringComparison.OrdinalIgnoreCase))
                    return QueryItems.GetValues(key).First(); // There might be multiple keys of the same name, but just return the first match
            }
            return null;
        }