C#: Remove duplicate values from dictionary?
Asked Answered
M

8

22

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?

Mcgough answered 22/9, 2009 at 19:27 Comment(3)
Which key do you want to keep? "1", "2", or none?Sprinkler
I think you need to give more information about the behavior of the unique-making function. For blue, which key should it keep, 1 or 2?Haematin
http://www.codeproject.com/KB/linq/Unique.aspxSuisse
G
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);
Gravitation answered 22/9, 2009 at 19:32 Comment(8)
Little does he know after 100K it loops back to 0, ruuhahaHanan
Thanks. The linq solution is what I was looking for. Curious could you somehow use the Distinct extension method?Mcgough
@User: I don't think Distinct would help in this case... DistinctBy from MoreLINQ would though.Gravitation
The LINQ method does not work in all situtions. Some dictionary sets do not allow you to call the .GroupBy() method.Limelight
@Mitchell: Could you give more details? What do you mean by "dictionary sets"?Gravitation
@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 outLimelight
@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<T> or a nongeneric collection?Gravitation
@Jon, yeah...I forgot to reference System.Linq. I should have known better. Thank you for your assistance!Limelight
D
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.

Dished answered 22/9, 2009 at 19:35 Comment(4)
I tried to imagine linq-like solution but didn't have VS at my fingertips. +1 for materializing this approach ;-)Micromillimeter
Didn't compile because I missed .Value after the First() call, but fixed it.Ustkamenogorsk
This didn't work for me. It created a dictionary with the "value" as both the key and the value.Mcgough
This produces a dictionary with the values as key and value! :-(Bitterling
A
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);
            }
        }
Arciniega answered 22/9, 2009 at 19:36 Comment(0)
M
1
foreach (var key in mydict.Keys)
  tempdict[mydict[key]] = key;
foreach (var value in tempdict.Keys)
  uniquedict[tempdict[value]] = value;
Micromillimeter answered 22/9, 2009 at 19:32 Comment(0)
K
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);
}
Kinslow answered 22/9, 2009 at 19:35 Comment(0)
E
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);
            }
        }
Expand answered 15/8, 2013 at 9:54 Comment(0)
B
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

Bribery answered 31/8, 2011 at 20:18 Comment(0)
A
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);
                     }
                 }
Armillda answered 10/9, 2015 at 10:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.