.ToSafeDictionary() extension method in csharp

To Safe Dictionary

The .ToDictionary() extension will throw an argument exception if we have duplicate keys added to the dictionary.

Ex:

List<int> listOfItems= new List<int>{1,1,2,3,4,4,5};
Dictionary<int, string> dict = listOfItems.ToDictionary(a => a, a => "item " + a);
Console.WriteLine(dict.Count);

The above code throws the following exception

System.ArgumentException: An item with the same key has already been added.

In this post, we’ll create an extension method which will ignore duplicate keys from being added to a dictionary.

So, though we add duplicates into the list of items and have it to transform to a dictionary, our extension method should ignore the duplicates and returns a dictionary without duplicates.

First, let’s see what is in the original .ToDictionary() extension method.

Original ToDictionary extension method source code

public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer) {
            if (source == null) throw Error.ArgumentNull("source");
            if (keySelector == null) throw Error.ArgumentNull("keySelector");
            if (elementSelector == null) throw Error.ArgumentNull("elementSelector");
            Dictionary<TKey, TElement> d = new Dictionary<TKey, TElement>(comparer);
            foreach (TSource element in source) d.Add(keySelector(element), elementSelector(element));
            return d;
        }

`.ToDictionary()` extension method is just a foreach over the enumerables. Each item in the list will be iterated and added to the dictionary. The accumulated dictionary is returned in the end.

ToSafeDictionary extension method

To implement .ToSafeDictionary() extension method, We’ll just extend the original .ToDictionary() method to have contains check before adding any item to the dictionary. No magic, just a conditional check to ignore the key.

public static Dictionary<TKey, TElement> ToSafeDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
    {
        if (source == null)
            throw new ArgumentException("source");
        if (keySelector == null)
            throw new ArgumentException("keySelector");
        if (elementSelector == null)
            throw new ArgumentException("elementSelector");
        Dictionary<TKey, TElement> d = new Dictionary<TKey, TElement>(comparer);
        foreach (TSource element in source)
        {
            if (!d.ContainsKey(keySelector(element)))
                d.Add(keySelector(element), elementSelector(element));
        }

        return d;
    }

Note that I’ve used the ArgumentException when the values for the parameters is null instead of using the original source code Error.ArgumentNull just to make sure we compile the code in C#.

Now if we run the same example code with .ToSafeDictionary on the list of items then we should not get any exceptions.

Dictionary<int, string> dict = listOfItems.ToSafeDictionary(a => a, a => "item " + a, null);
Console.WriteLine(dict.Count);

References