JBone
JBone

Reputation: 3203

.Net Dictionary with forced unique values

Is it possible to force a Dictionary to have unique values? See the following example.

Dictionary<string, string> types = new Dictionary<string, string>()
{
        {"1", "one"},
        {"2", "two"},
        {"3", "three"}
};

In the event some one tried to execute the following line, they should receive an error.

types.Add("4","one");

I know this is not how a dictionary is built to operate and the correct answer may be to use a different/custom data structure.

Upvotes: 3

Views: 9618

Answers (4)

Sriram Sakthivel
Sriram Sakthivel

Reputation: 73442

unfortunately Dictionary provided by framework doesn't provide this feature. Fastest workaround would be build something like this

public class UniqueValueDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    public new void Add(TKey key, TValue value)
    {
        if (this.ContainsValue(value))
        {
            throw new ArgumentException("value already exist");
        }
        base.Add(key, value);
    }

    public new TValue this[TKey key]
    {
        get
        {
            return base[key];
        }
        set
        {
            if (this.ContainsValue(value))
            {
                throw new ArgumentException("value already exist");
            }

            base[key] = value;
        }
    }
}

Or something like the following(which is better in performance but not memory)

public class UniqueValueDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    Dictionary<TValue, TKey> valueAsKey = new Dictionary<TValue, TKey>();

    public new void Add(TKey key, TValue value)
    {
        if (valueAsKey.ContainsKey(value))
        {
            throw new ArgumentException("value already exist");
        }
        base.Add(key, value);
        valueAsKey.Add(value, key);
    }

    public new TValue this[TKey key]
    {
        get
        {
            return base[key];
        }
        set
        {
            if (valueAsKey.ContainsKey(value))
            {
                throw new ArgumentException("value already exist");
            }

            if (!this.ContainsKey(key))
            {
                this.Add(key, value);
            }
            else
            {
                base[key] = value;
                valueAsKey[value] = key;
            }
        }
    }

    //You may need to handle remove as well
}

Note:this will work only when you use UniqueValueDictionary<TKey, TValue> type, If you cast to Dictionary<TKey, TValue> you can add duplicate values.

As pointed in comments you can build something like this inheriting from IDictionary<TKey, TValue> not Dictionary<TKey, TValue> taking this as an idea.

Upvotes: 3

aquinas
aquinas

Reputation: 23786

You probably want to implement IDictionary and internally just call the corresponding Dictionary<TKey,TValue> methods. Also, you want a HashSet<TValue>. And then, on your Add method you would first check to see if your hashset.Contains(value). If it does, then you throw an exception.

On the other hand, do you really NEED this behavior? What if you just use a HashSet<Tuple<string,string>>. Then, any duplicates are just ignored. Or do you really NEED the data structure to throw an exception? If you don't, that's what I would go with.

Edit: good point @Alexei Levenkov. If you will have the same value with different keys, then the HashSet approach doesn't give you what you originally asked for. That would only be applicable if you were expecting the SAME key/value pairs.

Upvotes: 6

Servy
Servy

Reputation: 203821

Keep two data structures; your regular dictionary and a HashSet<string> for the values. When you would like to add an item first check if the value is in the hash set. If it's not, then you know it's safe to add to both the dictionary and the set. (Also ensure you remove items from both collections on removal.)

If this is done in enough places then it may be worth creating your own IDictionary<K,V> implementation that uses both a regular Dictionary and a HashSet internally, so that you don't need to do so much work when using it. If this particular structure is only used in just a few places, it may not be worth the investment to create such a class.

Upvotes: 12

Venkata Krishna
Venkata Krishna

Reputation: 15112

Check for types.ContainsValue before adding

string str = "one";
if (!types.ContainsValue(str)) //doesn't add if its already there
{
    types.Add("4", str);
}

Upvotes: 4

Related Questions