How to get the duplicate key in a ToDictionary cast? [duplicate] - c#

This question already has answers here:
How do you get the duplicate key that ToDictionary() has failed on?
(4 answers)
Closed 7 years ago.
i have to cast a list to a dictionary in my app but im getting an error saying that "An item with the same key has already been added". But it is a list with more then 5k objects and i need to see wich objects are with the same key. Is there a way to do that?
In the message exception i cant get it, so i thought that i can do it using a foreach or something.
Any suggestions?
Thanks!
EDIT:
var targetDictionary = targetCollection.ToDictionary(k => k.Key);
This target collection is a generic IEnumerable, and the key i get from a thirdy party database so i do not have access to it. The solution is find the problematic object and tells the supplier about it.

You can use LINQ to catch the duplicates. You can then process them as you wish.
create a dictionary that doesn't contain the duplicates
var duplicates = myList.GroupBy(x => x.SomeKey).Where(x => x.Count() > 1);
var dictionaryWithoutDups = myList
.Except(duplicates.SelectMany(x => x))
.ToDictionary(x => x.SomeKey);
create a dictionary that contains only the first of each duplicate
var groups = myList.GroupBy(x => x.SomeKey);
var dictionaryWithFirsts = groups.Select(x => x.First()).ToDictionary(x => x.SomeKey);

var badGroups = collection.GroupBy(item => item.Key)
.Where(group => group.Count() > 1);
foreach (var badGroup in badGroups)
{
Console.WriteLine("The key {0} appears {1} times.", badGroup.Key, badGroup.Count());
forach (var badItem in badGroup)
{
Console.WriteLine(badItem);
}
}
var goodItems = collection.GroupBy(item => item.Key)
.Where(group => group.Count() == 1)
.SelectMany(group => group);
foreach (var goodItem in goodItems)
{
Console.WriteLine("The key {0} appears exactly once.", goodItem.Key);
}
var dictionary = goodItems.ToDictionary(item => item.Key);

If you want to keep the duplicates you can use .ToLookup instead. It creates a ILookup<TKey, TValue> which is basically a read-only Dictionary<TKey, IEnumerable<TValue>> where the duplicates are stored as the "value" in a collection.

If you are just looking for dups
HashSet<string> hs = new HashSet<string>();
foreach(string s in myList) if(!hs.Add(s)) Debug.WriteLine("dup: " + s);
Or you could change hs to Dictionary if you want to process
Dictionary<string, myclass> dl = new Dictionary<string, myclass>();
foreach(string s in myList)
{
if(dl.ContainsKey(s))
{
Debug.WriteLine("dup: " + s);
}
else
{
dl.Add(s, null);
}
}
I see you accepted a LINQ answer but LINQ is not going to out perform this.

Related

Getting more then one value in the same time from Dictionary

I have 2 dictionaries the first one has a key that are match multiple duplicate values in the second one:
var firstDic = new Dictionary<string, string>();
firstDic.Add("123", "Value1");
firstDic.Add("456", "Value2");
firstDic.Add("789", "Value3");
firstDic.Add("000", "Value4");
var secondDic = new Dictionary<string, string>();
secondDic.Add("5d866ac0-abab-46c9-a951-e7b0cf25dc72", "123");
secondDic.Add("217ddba3-977f-45b8-a7f6-80c6fbcef16e", "123");
secondDic.Add("99867f65-22c1-4b6c-b1bb-3fa73af317a9", "456");
secondDic.Add("203f9cf7-98f8-40fc-a4ba-da3a62c7d795", "456");
secondDic.Add("9bdafb4c-4d5a-4c87-8b9d-d9b98473390a", "789");
secondDic.Add("d3a245f0-cc5b-4c08-aaff-475d64e27e8d", "000");
I have a foreach in which i`m iterating over every key in the first dictionary
foreach (var item in firstDic)
{
//It`s only taking the first match in the secondDic
var myKey = secondDic.FirstOrDefault(x => x.Value == item.Key);
}
Is it possible to extract all the keys from the second dictionary(secondDic)that has the values matching from the first dictionary key and store them in some data structure.
Tnx in advice!
Then you don't need to use FirstOrDefault(), just use the Where() which will return a collection back :
var myKeys = secondDic.Where(x => x.Value == item.Key);
and then you can iterate over it:
foreach(var item in myKeys)
{
}
or do whatever business logic needs to be applied.
This may help
var result = secondDic.Where(s => firstDic.Any(f => f.Key == s.Value))
.Select(s => s.Key)
.ToList();

Use linq expression to filter a dictionary with a list of keys

I have a dictionary containing all users with their corresponding age.
Dictionary<string,int> AllUsers;
I have a list of specific users.
List<String> Users;
I would like to filter the first dictionary AllUsers with only the users who have their name in the SpecificUsers list.
I have done something manually with loops but I would like to use linq expression but I am not very familiar with them.
Thanks in advance for your help
You could filter Users:
Users.Where(i => AllUsers.ContainsKey(i)).Select(i => new { User = i, Age = AllUsers[i] });
The major benefit of this is that you're using the indexed AllUsers to do the filtering, so your total computational complexity only depends on the amount of users in Users (Dictionary.Contains is O(1)) - the naïve approaches tend to be Users * AllUsers.
If you want a dictionary on output, it's as simple as replacing the .Select(...) above with
.ToDictionary(i => i, i => AllUsers[i])
It might work
var newdict = AllUsers.Where(x => Users.Contains(x.Key))
.ToDictionary(val => val.Key, val => val.Value);
it will create new dictionary (cause linq is for querying not updating) with all the users from dictionary that are on the Users list. You need to use ToDictionary to actualy make it dictionary.
EDIT:
As #Rawling said it would be more performant to filter on Dictionary rather than on list. Solution to achieve that is present in #Luaan answer (I won't copy it as some do)
You can use a join() method to actually join the two collections. It allows us to get what you need with a single line of linq.
var allUsers = new Dictionary<string, int>();
allUsers.Add("Bob", 10);
allUsers.Add("Tom", 20);
allUsers.Add("Ann", 30);
var users = new List<string>();
users.Add("Bob");
users.Add("Tom");
users.Add("Jack");
var result = allUsers.Join(users, o => o.Key, i => i, (o, i) => o);
foreach(var r in result)
{
Console.WriteLine(r.Key + " " + r.Value);
}
It will output the following in the console:
Bob 10
Tom 20
Only the names that appears in both collection will be available in the result collection
There are multiple ways to do this
You can use this using where keyword
var result= yourDictionary.Where(p=> yourList.Contains(p.Key))
.ToDictionary(p=> p.Key, p=> p.Value);
But if you have lot of entries its better to use HashSet
var strings = new HashSet<string>(yourList);
var result= yourDictionary.Where(p=> strings.Contains(p.Key))
.ToDictionary(p=> p.Key, p=> p.Value);
using JOIN
var query =
from kvp in yourDictionary
join s in yourList on kvp.Key equals s
select new { kvp.Key, kvp.Value };
With the help of the following useful function
public static class Extensions
{
public static KeyValuePair<TKey,TValue>? Find<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key)
{
TValue value;
return source.TryGetValue(key, out value) ? new KeyValuePair<TKey, TValue>(key, value) : (KeyValuePair<TKey, TValue>?)null;
}
}
here is IMO the optimal solution (uses single lookup per key and does not introduce closure):
var filteredUsers = Users.Select(AllUsers.Find)
.Where(item => item.HasValue)
.ToDictionary(item => item.Value.Key, item => item.Value.Value);

C# get keys and values from List<KeyValuePair<string, string>

Given a list:
private List<KeyValuePair<string, string>> KV_List = new List<KeyValuePair<string, string>>();
void initList()
{
KV_List.Add(new KeyValuePair<string, string>("qwer", "asdf"));
KV_List.Add(new KeyValuePair<string, string>("qwer", "ghjk"));
KV_List.Add(new KeyValuePair<string, string>("zxcv", "asdf"));
KV_List.Add(new KeyValuePair<string, string>("hjkl", "uiop"));
}
(NOTE: there are multiple values for the key "qwer" and multiple keys for the value "asdf".)
1) Is there a better way to return a list of all keys than just doing a foreach on the KeyValuePair List?
2) Similarly, is there a better way to return a list of all values for a given key than using a foreach?
3) And then, how about returning a list of keys for a given value?
Thanks...
// #1: get all keys (remove Distinct() if you don't want it)
List<string> allKeys = (from kvp in KV_List select kvp.Key).Distinct().ToList();
// allKeys = { "qwer", "zxcv", "hjkl" }
// #2: get values for a key
string key = "qwer";
List<string> values = (from kvp in KV_List where kvp.Key == key select kvp.Value).ToList();
// values = { "asdf", "ghjk" }
// #3: get keys for a value
string value = "asdf";
List<string> keys = (from kvp in KV_List where kvp.Value == value select kvp.Key).ToList();
// keys = { "qwer", "zxcv" }
It sounds like you would benefit from using something like:
Dictionary<string, List<string>> kvlist;
kvlist["qwer"] = new List<string>();
kvlist["qwer"].Add("value1");
kvlist["qwer"].Add("value2");
foreach(var value in kvlist["qwer"]) {
// do something
}
It would be relatively easy to create a basic mutli-value dictionary class using a Dictionary and List.
This blog post talks more about Microsoft's MultiDictionary type available via NuGet.
Well you could definitly use your LINQ. But it's not "better" (in term of performance) since looping is already fast. It is perhaps more readable (personnal preference). For all the answers below, be aware that you need to have the System.Linq namespace imported. They also return IEnumerable<T> that are lazy loaded (executed when iterated over). If you want to return a concrete list, you can call the .ToList() extension.
Is there a better way to return a list of all keys than just doing a foreach on the KeyValuePair List?
KV_List.Select(kvp => kvp.Key);
Similarly, is there a better way to return a list of all values for a given key than using a foreach?
var theKeyToLookFor = "qwer";
KV_List.Where(kvp => kvp.Key == theKeyToLookFor).Select(kvp => kvp.Value);
And then, how about returning a list of keys for a given value?
var theValueToLookFor = "asdf";
KV_List.Where(kvp => kvp.Value == theValueToLookFor)
.Select(kvp => kvp.Value)
.ToList();
For more information on LINQ, look at LINQ (Language-Integrated Query)
You can use NameValueCollection from System.Collection.Specialized namespace:
NameValueCollection KV_List = new NameValueCollection();
KV_List.Add("qwer", "asdf");
KV_List.Add("qwer", "ghjk");
KV_List.Add("zxcv", "asdf");
KV_List.Add("hjkl", "uiop");
Example of use:
string singleValue = KV_List["zxcv"]; // returns "asdf"
string[] values = KV_List.GetValues("qwer"); // returns "asdf, "ghjk"
string[] allKeys = KV_List.AllKeys;
string[] allValues = KV_List.AllKeys;
https://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection%28v=vs.110%29.aspx
1:
KV_List.Select(i => i.Key).ToList()
2:
KV_List.Where(i => i.Key == filterByKey).Select(i => i.Value).ToList()
3:
KV_List.Where(i => i.Value == filterByValue).Select(i => i.Key).ToList()
I would use ILookup<K,V> in your case. It is like a dictionary but you can get the values as IEnumerable<V> with the same key.
ILookup<string, string> lookup = KV_List.ToLookup(x => x.Key, x => x.Value);
IEnumerable<string> list = lookup["qwer"];
foreach(string str in list)
{
Console.WriteLine(str);
}
or simply
Console.WriteLine(string.Join(",", lookup["qwer"]));
Are you reading this and wondering why someone made some code have an IEnumerable<KeyValuePair<A,B>> instead of a Dictionary<A,B> but don't feel like asking and just want to get done?
if (collection == null)
return null;
return collection
.Where(z => z.Key == aThing)
.Select(z => z.Value)
.FirstOrDefault();

Exception upon trimming the extension of key values of dictionary

I have a dictionary with key values and I am trying to trim the extension of these values but get the exception message that item with same key has already been added.
Not sure why it is the case.
This is the code I used
How can I overcome this problem?
dictFilesNotThere = dictFilesNotThere.ToDictionary
(t => t.Key.Remove(8, 3), t => t.Value);
The key value is as follow '7dim-058-ns' and I trying to get it to '7dim-058' instead
Suppose you have following items in the dictionary:
dictFilesNotThere.Add("7dim-058-ns", 1);
dictFilesNotThere.Add("7dim-058-n2", 2);
dictFilesNotThere.Add("7dim-058-n3", 2);
Then after removing via t.Key.Remove(8, 3) you will get: 7dim-058 as the key for all the above items. Since in the dictionary you can't have duplicate keys, that is why the exception.
To get rid of the problem, you can setup a counter and add that to the key, if the key is found in the dictionary before. Something like:
Dictionary<string, int> dictFilesNotThereCopy = new Dictionary<string, int>();
int counter = 0;
foreach (KeyValuePair<string,int> item in dictFilesNotThere)
{
if (dictFilesNotThereCopy.ContainsKey(item.Key.Remove(8, 3)))
dictFilesNotThereCopy.Add((item.Key.Remove(8, 3) + (counter++)).ToString(), item.Value);
else
dictFilesNotThereCopy.Add(item.Key.Remove(8, 3), item.Value);
}
If the same key already exists you have to specify which you want to keep, for example the first:
dictFilesNotThere.Select(kv => new { kv, newkey = kv.Key.Substring(0, 8) })
.GroupBy(x => x.newkey)
.ToDictionary(g => g.Key, g => g.First().kv.Value);

Merging Dictionary containing a List in C#

This is kind-of related to this question, on how to merge two dictionaries in C#. An elegant Linq solution is presented, which is cool.
However, that question relates to Dictionary<Object1, Object2>, whereas I have a dictionary where the value is a List<Object2>.
I am looking for a solution for merging a Dictionary<Object1, List<Object2>>, with the following requirements:
If Dictionary1 contains the same key as Dictionary2, then their List<Object2> lists should be combined. You would end up with a new key-value-pair with the shared key, and the combined lists from the two dictionaries.
If Dictionary1 contains a key that Dictionary2 doesn't then the List<Object2> list from Dictionary1 should become the value, and vice versa.
This may not be possible in Linq, or it may be worth writing it out longhand with for loops and the like, but it would be nice to have an elegant solution.
I would suggest creating your own extension method. It will be more efficient and easier to modify.
public static void MergeDictionaries<OBJ1, OBJ2>(this IDictionary<OBJ1, List<OBJ2>> dict1, IDictionary<OBJ1, List<OBJ2>> dict2)
{
foreach (var kvp2 in dict2)
{
// If the dictionary already contains the key then merge them
if (dict1.ContainsKey(kvp2.Key))
{
dict1[kvp2.Key].AddRange(kvp2.Value);
continue;
}
dict1.Add(kvp2);
}
}
The difficulty is dealing with the merging of key conflicts.
If we start by flattening all the input dictionaries using SelectMany, we can group together the elements by their key.
var result = dictionaries
.SelectMany(dict => dict)
.GroupBy(kvp => kvp.Key)
The result set contains groups where each group's key is a key from the original dictionaries, and the contents of the group are an IEnumerable<List<T>> of the lists with the same key. From these groups, we can merge all List<T> into a single IEnumerable<T> using a Select transformation with SelectMany.
var result = dictionaries
.SelectMany(dict => dict)
.GroupBy(kvp => kvp.Key)
.Select(grp => new { Key = grp.Key, Items = grp.SelectMany(list => list)})
We can then get a dictionary from this using a ToDictionary transformation, converting the IEnumerable<T> back to a List<T>.
var result = dictionaries
.SelectMany(dict => dict)
.GroupBy(kvp => kvp.Key)
.Select(grp => new { Key = grp.Key, Items = grp.SelectMany(list => list)})
.ToDictionary(kip => kip.Key, kip => new List<T>(kip.Items));
Updated in response to comment
You can populate dictionaries however you like. I have assumed it is a type which implements IEnumerable<IDictionary<TKey, List<T>>> for a TKey and T of your choosing.
The simplest way would be using a List<T> as follows:
List<IDictionary<TKey, List<T>>> dictionaries
= new List<IDictionary<TKey, List<T>>>();
dictionaries.Add(dictionary1); // Your variable
dictionaries.Add(dictionary2); // Your variable
// Add any other dictionaries here.
// Code as above!
You just need to change item merging part in solution to the previous problem.
For object we have this:
.ToDictionary(group => group.Key, group => group.First())
i.e. for duplicated items, just take the first one.
But we could use this:
.ToDictionary(group => group.Key, group => group.SelectMany(list => list).ToList());
to concatenate lists.
So, the final expression would be
var result = dictionaries.SelectMany(dict => dict)
.ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key,
group => group.SelectMany(list => list).ToList());
You could try a different merging expression if you need some extra list merging logic (e.g. only merge distinct items)
I'll be the first to admit that this is not all that pretty but this works for me.
var d1 = new Dictionary<string, List<string>>();
var d2 = new Dictionary<string, List<string>>();
d1["test"] = new List<string>() { "Stockholm", "Motala" };
d1["more"] = new List<string>() { "numerous", "populous", "bigger", "plentiful" };
d2["test"] = new List<string>() { "Washington", "Charlottesville" };
d2["less"] = new List<string>() { "insufficient", "small", "imperceptible" };
var intersect = (from key in d1.Keys.Intersect(d2.Keys) select new { Key = key, Value = new List<string>(d1[key].Concat(d2[key])) }).ToDictionary(d => d.Key, d => d.Value);
var merged = d1.Concat(d2).Where(d => !intersect.Keys.Contains(d.Key)).Concat(intersect).ToDictionary(d => d.Key, d => d.Value);

Categories

Resources