23

How can I create a dictionary with no duplicate values from a dictionary that may have duplicate values?

IDictionary<string, string> myDict = new Dictionary<string, string>();

myDict.Add("1", "blue");
myDict.Add("2", "blue");
myDict.Add("3", "red");
myDict.Add("4", "green");


uniqueValueDict = myDict.???

Edit:

-I don't care which key is kept. - Is there something using Distinct() operation?

User
  • 62,498
  • 72
  • 186
  • 247

8 Answers8

51

What do you want to do with the duplicates? If you don't mind which key you lose, just build another dictionary like this:

IDictionary<string, string> myDict = new Dictionary<string, string>();

myDict.Add("1", "blue");
myDict.Add("2", "blue");
myDict.Add("3", "red");
myDict.Add("4", "green");

HashSet<string> knownValues = new HashSet<string>();
Dictionary<string, string> uniqueValues = new Dictionary<string, string>();

foreach (var pair in myDict)
{
    if (knownValues.Add(pair.Value))
    {
        uniqueValues.Add(pair.Key, pair.Value);
    }
}

That assumes you're using .NET 3.5, admittedly. Let me know if you need a .NET 2.0 solution.

Here's a LINQ-based solution which I find pleasantly compact...

var uniqueValues = myDict.GroupBy(pair => pair.Value)
                         .Select(group => group.First())
                         .ToDictionary(pair => pair.Key, pair => pair.Value);
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 12
    Little does he know after 100K it loops back to 0, ruuhaha – SwDevMan81 Sep 22 '09 at 20:28
  • Thanks. The linq solution is what I was looking for. Curious could you somehow use the Distinct extension method? – User Sep 22 '09 at 20:55
  • @User: I don't think Distinct would help in this case... DistinctBy from MoreLINQ would though. – Jon Skeet Sep 22 '09 at 21:27
  • The LINQ method does not work in all situtions. Some dictionary sets do not allow you to call the .GroupBy() method. – Mitchell Skurnik Oct 14 '10 at 17:29
  • @Mitchell: Could you give more details? What do you mean by "dictionary sets"? – Jon Skeet Oct 14 '10 at 17:35
  • @Jon I am not sure exactly. I am pulling a ObservableCollection from nested usercontrol and it does not allow me to use .Distinct or .Select and so on. I even tried converting that to a Dictionary/IDictionary and then trying to get the distinct. This still didnt work. I ended up doing a foreach loop on each item and then using the hashset to get detect if it is a duplicate. I then created the new comboboxitems (silverlight project) and added them to the combobox. This was .Net 4/Silverlight 4. While the LINQ method would have been ideal, I am just lucky that your other method worked out – Mitchell Skurnik Oct 14 '10 at 17:49
  • @Mitchell: There are various reasons for this, but it's hard to delve into them in comments. Please start a new question, and give more details. For example, make sure that you've got a reference to System.Core.dll, and a using directive for System.Linq. Also, is this the generic `ObservableCollection` or a nongeneric collection? – Jon Skeet Oct 14 '10 at 18:08
  • @Jon, yeah...I forgot to reference System.Linq. I should have known better. Thank you for your assistance! – Mitchell Skurnik Oct 14 '10 at 18:31
9

The brute-force solution would be something like the following

var result = dictionary
    .GroupBy(kvp => kvp.Value)
    .ToDictionary(grp => grp.First().Value, grp.Key)

assuming you don't really care about the key used to represent a group of duplicates and it is acceptable to rebuild the dictionary.

Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143
3

Jon beat me to the .NET 3.5 solution, but this should work if you need a .NET 2.0 solution:

        List<string> vals = new List<string>();
        Dictionary<string, string> newDict = new Dictionary<string, string>();
        foreach (KeyValuePair<string, string> item in myDict)
        {
            if (!vals.Contains(item.Value))
            {
                newDict.Add(item.Key, item.Value);
                vals.Add(item.Value);
            }
        }
Timothy Carter
  • 15,459
  • 7
  • 44
  • 62
1
foreach (var key in mydict.Keys)
  tempdict[mydict[key]] = key;
foreach (var value in tempdict.Keys)
  uniquedict[tempdict[value]] = value;
queen3
  • 15,333
  • 8
  • 64
  • 119
1
Dictionary<string, string> test = new Dictionary<string,string>();
test.Add("1", "blue");
test.Add("2", "blue");
test.Add("3", "green");
test.Add("4", "red");
Dictionary<string, string> test2 = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> entry in test)
{
    if (!test2.ContainsValue(entry.Value))
        test2.Add(entry.Key, entry.Value);
}
Sivvy
  • 853
  • 9
  • 28
1

This is how I did it:

                dictionary.add(control, "string1");
                dictionary.add(control, "string1");
                dictionary.add(control, "string2");
              int x = 0;
        for (int i = 0; i < dictionary.Count; i++)
        {         
            if (dictionary.ElementAt(i).Value == valu)
            {
                x++;
            }
            if (x > 1)
            {
                dictionary.Remove(control);
            }
        }
MILAD
  • 649
  • 1
  • 5
  • 7
0

Just a footnote to those using the Revit API, this is one method that works for me in removing duplicate elements, when you can't use say wallType as your object type and instead need to leverage raw elements. it's a beaut mate.

  //Add Pair.value to known values HashSet
                 HashSet<string> knownValues = new HashSet<string>();

                Dictionary<Wall, string> uniqueValues = new Dictionary<Wall, string>();

                 foreach (var pair in wall_Dict)
                 {
                     if (knownValues.Add(pair.Value))
                     {
                         uniqueValues.Add(pair.Key, pair.Value);
                     }
                 }
0

In addition to the answer of Jon Skeet , if your value is an intern object you can use :

var uniqueValues = myDict.GroupBy(pair => pair.Value.Property)
                     .Select(group => group.First())
                     .ToDictionary(pair => pair.Key, pair => pair.Value);

This way you will remove the duplicate only on one property of the object

Guillaume V
  • 931
  • 2
  • 10
  • 27