Merging 2 dictionaries having duplicate keys with linq - c#

How to merge 2 dictionaries of IDictionary<Guid, MyObject> where MyObject is a class instance?
IDictionary<Guid, MyObject> d1 = new Dictionary<Guid, MyObject>();
d1.Add(guid1, m1);
d1.Add(guid2, m2);
d1.Add(guid3, m3);
IDictionary<Guid, MyObject> d2 = new Dictionary<Guid, MyObject>();
d2.Add(guid2, m2);
d2.Add(guid3, m3);
d2.Add(guid4, m4);
IDictionary<Guid, MyObject> d3 = d1.Union(d2) ???
That in d3 there are the following entries:
guid1,m1
guid2,m2
guid3,m3
guid4,m4

d1.Concat(d2.Where( x=> !d1.Keys.Contains(x.Key)));

d1.Union(d2).GroupBy (kvp => kvp.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.First().Value); out to do the trick.
IDictionary<Guid, MyObject> d1 = new Dictionary<Guid, MyObject>();
d1.Add(guid1, m1);
d1.Add(guid2, m2);
d1.Add(guid3, m3);
IDictionary<Guid, MyObject> d2 = new Dictionary<Guid, MyObject>();
d2.Add(guid2, m2);
d2.Add(guid3, m3);
d2.Add(guid4, m4);
IDictionary<Guid, MyObject> d3 =
d1.Union(d2).GroupBy (kvp => kvp.Key)
.ToDictionary (kvp => kvp.Key, kvp => kvp.First ().Value);

If you have duplicate key then you'll have to handle duplicate key with the using of where clause.
var result = d1.Union(d2.Where(k => !d1.ContainsKey(k.Key))).ToDictionary(k => k.Key, v => v.Value)
Note : It will not get duplicate key. if there will be any duplicate key than it will get d1's key.

You could try something like
d1.Concat(d2).Distinct(kv => kv.Key).ToDictionary(kv => kv.Key, kv => kv.Value)
The result of concat makes use of the fact that the dictionary is an IEnumerable<KeyvaluePair<Guid,MyObject>>
Since I do not have a compiler I just checked that Distinct cannot accept just a lambda selecting the property to be compared. However it can accept an EqualityComparer. What I often have in projects is a Generic Equality Comparer that allows to pass in lambdas which define the equality operation.

Union looks good:
http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#union1

This will merge unique keys and unique list of values:
d1.Union(d2).GroupBy(g => g.Key).ToDictionary(pair => pair.Key, pair => pair.First().Value.Union(pair.Last().Value).ToList());

When no duplicates keys exist, the following works for 2 (or more) dictionaries:
var dictionaries = new [] { d1, d2 };
var result = dictionaries.SelectMany(dict => dict)
.ToDictionary(pair => pair.Key, pair => pair.Value);

Related

Filter dictionary on the basis of key value present in list

I have a dictionary D1 like this
and a list L1 like this
I want to have a dictionary like this (Filtering those key-value pairs whose key is present in the list )
So, I tried D1.Where(x => L1.Contains(x.Key)) but i got a dictionary of 2 rows with empry string in key and value.
Please advise.
You can use some thing like this :
Dictionary<string, int> dictionary = new Dictionary<string, int> {{"A", 1}, {"B", 2}, {"C", 3}};
List<string> list = new List<string> {"A","B"};
var result = dictionary.Where(x => list.Contains(x.Key)).ToList();
or
var result = dictionary.Where(x => list.Contains(x.Key)).ToDictionary(x=>x.Key,x=>x.Value);
There are multiple ways to achieve what you need
In case all keys of D1 are present in L1 without mutating D1
Dictionary<string, string> D2 = new Dictionary<string, string>();
L1.ForEach(x => D2[x] = D1[x]);
OR
var D2 = L1.ToDictionary(el => el, key => D1[key]);
Safe option:
var D2 = D1.Keys.Intersect(L1).ToDictionary(key => key, key => D1[key]);
and even more, depends on your creativity
Note that this is slow for big list and dictionary
D1.Where(x => L1.Contains(x.Key))

connect values with the same key in Dictionary<string, string>

I need to combine 2 dictionary. they have the same key (KKK) with different values:
Dictionary<string, string> dic1 = ...Load***();
Dictionary<string, string> dic2 = ...Load***();
dic1:
...
[29] {[RCP, 555501001]}
[30] {[KKK, 04611105012042000120]}
...
dic2:
...
[49] {[SUM, 85737]}
[50] {[KKK, 04611701040040000180]}
...
Union:
Dictionary<string, string> dicUnion= dic1.Union(dic2).OrderBy(k => k.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
result:
ThrowArgumentException: The item with the same key has already been added.
I have connect values with the same key in union Dictionary:
...
[29] {[RCP, "555501001"]}
[30] {[KKK, "04611105012042000120 04611701040040000180"]}
[31] {[SUM, "85737"]}
...
If you really want to use LINQ in this case(i'd prefer a loop):
var dicUnion = dic1.Concat(dic2)
.GroupBy(kv => kv.Key)
.ToDictionary(g => g.Key, g => String.Join(" ", g.Select(kv => kv.Value)));
This will merge both dictionaries but don't care about common keys or different values.
I need to combine 2 dictionary, they have the same key (KKK) with
different values
Ok, if you only want to create a dictionary of common keys:
var union = from kv1 in dic1
join kv2 in dic2
on kv1.Key equals kv2.Key into keyGroup
where keyGroup.Any()
select new KeyValuePair<string, string>(kv1.Key, string.Join(" ", keyGroup.Select(kv => kv.Value)));
Dictionary<string, string> dicUnion = union.ToDictionary(kv => kv.Key, kv => kv.Value);
But instead of concatenating values which have the same key in both dictionaries, i'd use a different approach. Use a Lookup<TKey, TValue>. For example:
var keyLookup = dic1.Concat(dic2).ToLookup(kv => kv.Key, kv => kv.Value);
Now you can do wahtever you want with the values, f.e. create a List<string>:
List<string> values = keyLookup["KKK"].ToList();
I'd use a simple loop to add / append items
Dictionary<string, string> dicUnion = new Dictionary<string, string>(dic1);
foreach(var item in dic2)
{
if(dicUnion.ContainsKey(item.Key))
{
dicUnion[item.Key] += " " + item.Value;
}
else
{
dicUnion.Add(item.Key, item.Value);
}
}
Just try like this;
var intersectedItems = dic1.Where(x => dic2.ContainsKey(x.Key)).Select(x => new
{
Key = x.Key,
Value = x.Value + " " + dic2[x.Key]
}).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
var dicUnion = dic1.Where(x => !intersectedItems.ContainsKey(x.Key))
.Union(dic2.Where(x => !intersectedItems.ContainsKey(x.Key)))
.Union(intersectedItems).OrderBy(k => k.Key)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
If you want to connect ONLY values with the same key, you can use IEnumerable.Join method like this:
var res = dic1.Join(dic2, o1 => o1.Key, o2 => o2.Key, (o1, o2) => new { o1.Key, Value1 = o1.Value, Value2 = o2.Value});
The arguments are the second IEnumerable, key selectors and result selector. In my snippet I create anonymous class but you can create whatever structure you want. The result is again IEnumerable, you can either iterate on or use its ToList(), ToArray(), etc method.
EDIT: after reading comments to other posts I understand you want to get something like this:
dic1.Concat(dic2)
.ToLookup(o => o.Key, o => o.Value)
.ToDictionary(o => o.Key, o => string.Join(" ", o.Distinct()))

C# How to make a list of Dictionaries into array

I have a method which accept params of Dictionary
static IDictionary<double, double> Merge(params Dictionary<double, double>[] dicts)
{
return dicts.SelectMany(p => p).ToLookup(p => p.Key, p => p.Value)
.ToDictionary(p => p.Key, p => p.Max());
}
I can use var r = Merge(r1,r2,r3); and it will work fine. r1,r2,r3 are dictionaries.
The problem is I don't know the number of Dictionaries I have, it fluctuates , sometimes 3, sometimes 30, or 300. I have the dictionaries in a list. Actualy I have a list of a class. so somehow I have to make it work in a foreach loop?
List<Class1> c = new List<Class1>();
class Class1
{
public Dictionary<Double, Double> r1 = new Dictionary<Double, Double>();
}
You have a method that accepts an array of dictionaries, so you only need to turn your list into an array.
var result = Merge(listOfDictionaries.ToArray());
after the original question was edited, now a update that should fix your problem. The new function merge takes an IEnumerable of Class1 and merges it to a Dictionary. It uses the Merge(IEnumerable<Dictionary<double, double>> dicts) the is defined below:
static Dictionary<Double, Double> Merge(IEnumerable<Class1> classes)
{
return Merge(classes.Select(c => c.r1));
}
Original Answer:
a small change in your parameter type definition should do it
static IDictionary<double, double> Merge(IEnumerable<Dictionary<double, double>> dicts)
{
return dicts.SelectMany(p => p)
.ToLookup(p => p.Key, p => p.Value)
.ToDictionary(p => p.Key, p => p.Max());
}
now you can just pass a list of dictionaries
EDIT
If you want to use both variations, you can do this like this
static IDictionary<double, double> Merge(params Dictionary<double, double>[] dicts)
{
return Merge((IEnumerable<Dictionary<double, double>>)dicts);
}

Extracting a Dictionary<TKey, double> from a Dictionary<TKey, string> instance

I have a generic Dictionary where TValue is of type String (Dictionary<int, string>). I chose to use string as the value type because the data was loaded from an Xml file where the source values can be character or numeric data types (I suppose Object would've been an acceptable TValue type too, but even then this question would be equally applicable).
The character data types also have importance, so excluding them outright isn't an option.
I'd like to extract a subset of this Dictionary<int, double>. In other words, I'd like the subset of the dictionary where the values are numeric.
Right now I'm doing it like this:
Dictionary<int, string> myDictionary;
// Do some loading.
var numericData = myDictionary.Where(kvp => Double.TryParse(kvp.Value, out temp)
This approach is awfully ugly and doesn't get me the result as a Dictionary<int, double> Can anyone offer other ways to improve this?
Thanks!
The code you've given is not only ugly - it will fail with an InvalidCastException at execution time. I suspect you actually want:
var numericData = myDictionary
.Select(kvp => {
double value;
return new { kvp.Key,
Value = double.TryParse(kvp.Value, out value)
? value : (double?) null
};
})
.Where(pair => pair.Value != null)
.ToDictionary(pair => pair.Key, pair => pair.Value.Value);
And yes, that's ugly - but:
It avoids parsing the value more than once
It avoids putting side-effects in your query
You can make it slightly cleaner but less efficient if you're happy to parse twice:
var numericData = myDictionary
.Where(kvp => { double tmp; return double.TryParse(kvp.Value, out tmp); })
.ToDictionary(pair => pair.Key, pair => double.Parse(pair.Value));
Or (more cleanly) you could create a separate method:
public static double? TryParseNullableDouble(string text)
{
double value;
return double.TryParse(text, out value) ? value : (double?) null;
}
Then the first version becomes:
var numericData = myDictionary
.Select(kvp => new { kvp.Key, TryParseNullableDouble(kvp.Value) })
.Where(pair => pair.Value != null)
.ToDictionary(pair => pair.Key, pair => pair.Value.Value);
You can just create a new dictionary using the temp variable which contains the double value - this exploits the fact that the enumeration and addition to the dictionary is done item by item so temp contains the correct double value:
double temp = 0;
var numDict = myDictionary.Where(kvp => Double.TryParse(kvp.Value, out temp))
.ToDictionary( x=> x.Key, x=> temp);

Convert List<MyObject> to Dictionary <obj.string, List<obj.ID>>

I would like to take a list of objects and convert it to a dictionary where the key is a field in the object, and the value is a list of a different field in the objects that match on the key. I can do this now with a loop but I feel this should be able to be accomplished with linq and not having to write the loop. I was thinking a combination of GroupBy and ToDictionary but have been unsuccessful so far.
Here's how I'm doing it right now:
var samplesWithSpecificResult = new Dictionary<string, List<int>>();
foreach(var sample in sampleList)
{
List<int> sampleIDs = null;
if (samplesWithSpecificResult.TryGetValue(sample.ResultString, out sampleIDs))
{
sampleIDs.Add(sample.ID);
continue;
}
sampleIDs = new List<int>();
sampleIDs.Add(sample.ID);
samplesWithSpecificResult.Add(sample.ResultString, sampleIDs);
}
The farthest I can get with .GroupBy().ToDictionay() is Dictionary<sample.ResultString, List<sample>>.
Any help would be appreciated.
Try the following
var dictionary = sampleList
.GroupBy(x => x.ResultString, x => x.ID)
.ToDictionary(x => x.Key, x => x.ToList());
The GroupBy clause will group every Sample instance in the list by its ResultString member, but it will keep only the Id part of each sample. This means every element will be an IGrouping<string, int>.
The ToDictionary portion uses the Key of the IGrouping<string, int> as the dictionary Key. IGrouping<string, int> implements IEnumerable<int> and hence we can convert that collection of samples' Id to a List<int> with a call to ToList, which becomes the Value of the dictionary for that given Key.
Yeah, super simple. The key is that when you do a GroupBy on IEnumerable<T>, each "group" is an object that implements IEnumerable<T> as well (that's why I can say g.Select below, and I'm projecting the elements of the original sequence with a common key):
var dictionary =
sampleList.GroupBy(x => x.ResultString)
.ToDictionary(
g => g.Key,
g => g.Select(x => x.ID).ToList()
);
See, the result of sampleList.GroupBy(x => x.ResultString) is an IEnumerable<IGrouping<string, Sample>> and IGrouping<T, U> implements IEnumerable<U> so that every group is a sequence of Sample with the common key!
Dictionary<string, List<int>> resultDictionary =
(
from sample in sampleList
group sample.ID by sample.ResultString
).ToDictionary(g => g.Key, g => g.ToList());
You might want to consider using a Lookup instead of the Dictionary of Lists
ILookup<string, int> idLookup = sampleList.ToLookup(
sample => sample.ResultString,
sample => sample.ID
);
used thusly
foreach(IGrouping<string, int> group in idLookup)
{
string resultString = group.Key;
List<int> ids = group.ToList();
//do something with them.
}
//and
List<int> ids = idLookup[resultString].ToList();
var samplesWithSpecificResult =
sampleList.GroupBy(s => s.ResultString)
.ToDictionary(g => g.Key, g => g.Select(s => s.ID).ToList());
What we 're doing here is group the samples based on their ResultString -- this puts them into an IGrouping<string, Sample>. Then we project the collection of IGroupings to a dictionary, using the Key of each as the dictionary key and enumerating over each grouping (IGrouping<string, Sample> is also an IEnumerable<Sample>) to select the ID of each sample to make a list for the dictionary value.

Categories