C# How to make a list of Dictionaries into array - c#

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);
}

Related

Build Dictionary with LINQ

Let's say we have a variable 'data' which is a list of Id's and Child Id's:
var data = new List<Data>
{
new()
{
Id = 1,
ChildIds = new List<int> {123, 234, 345}
},
new()
{
Id = 1,
ChildIds = new List<int> {123, 234, 345}
},
new()
{
Id = 2,
ChildIds = new List<int> {678, 789}
},
};
I would like to have a dictionary with ChildId's and the related Id's. If the ChildId is already in the dictionary, it should overwrite with the new Id.
Currently I have this code:
var dict = new Dictionary<int, int>();
foreach (var dataItem in data)
{
foreach (var child in dataItem.ChildIds)
{
dict[child] = dataItem.Id;
}
}
This works fine, but I don't like the fact that I am using two loops. I prefer to use Linq ToDictionary to build up the dictionary in a Functional way.
What is the best way to build up the dictionary by using Linq?
Why? I prefer functional code over mutating a state. Besides that, I was just curious how to build up the dictionary by using Linq ;-)
In this case your foreach appproach is both, readable and efficient. So even if i'm a fan of LINQ i would use that. The loop has the bonus that you can debug it easily or add logging if necessary(for example invalid id's).
However, if you want to use LINQ i would probably use SelectMany and ToLookup. The former is used to flatten child collections like this ChildIds and the latter is used to create a collection which is very similar to your dictionary. But one difference is that it allows duplicate keys, you get multiple values in that case:
ILookup<int, int> idLookup = data
.SelectMany(d => d.ChildIds.Select(c => (Id:d.Id, ChildId:c)))
.ToLookup(x => x.ChildId, x => x.Id);
Now you have already everything you needed since it can be used like a dictionary with same lookup performance. If you wanted to create that dictionary anyway, you can use:
Dictionary<int, int> dict = idLookup.ToDictionary(x => x.Key, x => x.First());
If you want to override duplicates with the new Id, as mentioned, simply use Last().
.NET-Fiddle: https://dotnetfiddle.net/mUBZPi
The SelectMany linq operator actually has a few less known overloads. One of these has a result collector which is a perfect use case for your scenario.
Following is an example code snippet to turn that into a dictionary. Note that I had to use the Distinct, since you had 2 id's with value 1 which had some duplicated child id's which would pose problems for a dictionary.
void Main()
{
// Get the data
var list = GetData();
// Turn it into a dictionary
var dict = list
.SelectMany(d => d.ChildIds, (data, childId) => new {data.Id, childId})
.Distinct()
.ToDictionary(x => x.childId, x => x.Id);
// show the content of the dictionary
dict.Keys
.ToList()
.ForEach(k => Console.WriteLine($"{k} {dict[k]}"));
}
public List<Data> GetData()
{
return
new List<Data>
{
new Data
{
Id = 1,
ChildIds = new List<int> {123, 234, 345}
},
new Data
{
Id = 1,
ChildIds = new List<int> {123, 234, 345}
},
new Data
{
Id = 2,
ChildIds = new List<int> {678, 789}
},
};
}
public class Data
{
public int Id { get; set; }
public List<int> ChildIds { get; set; }
}
The approach is to create pairs of each combination of Id and ChildId, and build a dictionary of these:
var list = new List<(int Id, int[] ChildIds)>()
{
(1, new []{10, 11}),
(2, new []{11, 12})
};
var result = list
.SelectMany(pair => pair.ChildIds.Select(childId => (childId, pair.Id)))
.ToDictionary(p => p.childId, p => p.Id);
ToDictionary will throw if there are duplicate keys, to avoid this you can look at this answer and create your own ToDictionary:
public static Dictionary<K, V> ToDictionaryOverWriting<TSource, K, V>(
this IEnumerable<TSource> source,
Func<TSource, K> keySelector,
Func<TSource, V> valueSelector)
{
Dictionary<K, V> output = new Dictionary<K, V>();
foreach (TSource item in source)
{
output[keySelector(item)] = valueSelector(item);
}
return output;
}
With LINQ you can achieve the result like this:
Dictionary<int, int> dict = (from item in data
from childId in item.ChildIds
select new { item.Id, childId}
).Distinct()
.ToDictionary(kv => kv.childId, kv => kv.Id);
Update:
Fully compatible version with foreach loop would use group by with Last(), instead of Distict():
Dictionary<int, int> dict2 = (from item in data
from childId in item.ChildIds
group new { item.Id, childId } by childId into g
select g.Last()
).ToDictionary(kv => kv.childId, kv => kv.Id);
As some already pointed out, depending on order of input elements does not feel "functional". LINQ expression becomes more convoluted then original foreach loop.
There is an overload of SelectMany which not only flattens the collection but also allows you to have any form of result.
var all = data.SelectMany(
data => data.ChildIds, //collectionSelector
(data, ChildId) => new { data.Id, ChildId } //resultSelector
);
Now if you want to transform all into a Dictionary, you have to remove the duplicate ChildIds first. You can use GroupBy as in below, and then pick the last item from each group (as you stated in your question you want to overwrite Ids as you go). The key of your dictionary should also be unique=ChildId:
var dict = all.GroupBy(x => x.ChildId)
.Select(x => x.Last())
.ToDictionary(x => x.ChildId, x => x.Id);
Or you can write a new class with IEquatable<> implemented and use it as the return type of resultSelector (instead of new { data.Id, ChildId }). Then write all.Reverse().Distinct().ToDictionary(x => x.ChildId); so it would detect duplicates based on your own implementation of Equals method. Reverse, because you said you want the last occurrence of the duplicates.

Merging 2 dictionaries having duplicate keys with linq

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);

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.

How can I Remove items from dictionary using lambda expression

I am not into LINQ solutions,
I am using simple predicat to determine if the key should be removed,
For example if the dictionary is construct like Dictionary<int, int>,
so how should I remove all the entries with negative data
I am prefer to use the same dictionary, not to create new one,
I don't have preformance issues
Is there a way to do it, without using LINQ, but using Lambda expressions?
I didn't want solutions in LINQ because no one is using them in my project, didn't want to be the first.., but because I saw the LINQ solutions look better, I will use them them..
The simplest way is probably to create a new dictionary, if that's okay for you:
var newDictionary = oldDictionary.Where(pair => pair.Value >= 0)
.ToDictionary(pair => pair.Key,
pair => pair.Value);
If you have to mutate the existing dictionary (e.g. because several other objects have reference to the same dictionary) you'd need to build a list of keys to remove, then remove them afterwards:
var toRemove = dictionary.Where(pair => pair.Value < 0)
.Select(pair => pair.Key)
.ToList();
foreach (var key in toRemove)
{
dictionary.Remove(key);
}
EDIT: I've just noticed the first sentence: "I am not into LINQ solutions". If that means you don't want to use a LINQ solution, here's the by-hand version:
List<int> toRemove = new List<int>();
foreach (KeyValuePair<int, int> pair in dictionary)
{
if (pair.Value < 0)
{
toRemove.Add(pair.Key);
}
}
foreach (var key in toRemove)
{
dictionary.Remove(key);
}
... but if you can use LINQ, I'd encourage you do. My second solution is equivalent to the "by-hand" version, but more readable IMO.
By merely using lambda expression:
foreach (var i in myDict.Where(d => (d.Value < 0 || d.key <0)).ToList() )
{
myDict.Remove(i.Key);
}
var toRemove = dict.Keys.Where(predicate).ToArray();
foreach (var key in toRemove) {
dict.Remove(key);
}
Well if you add
namespace MMExtensions
{
public static class DictionaryExtensions
{
public delegate bool Predicate<TKey, TValue>(KeyValuePair<TKey, TValue> d);
[MethodImpl(MethodImplOptions.Synchronized)]
public static void Filter<TKey, TValue>(
this Dictionary<TKey, TValue> hashtable, Predicate<TKey, TValue> p)
{
foreach (KeyValuePair<TKey, TValue> value in hashtable.ToList().Where(value => !p(value)))
hashtable.Remove(value.Key);
}
}
}
And you had some dataset as dictionary:
Dictionary<string, int> d =
new Dictionary<string, int> {{"v", -3}, {"val1", 1}, {"val2", 2}};
Then you could use:
d.Filter(delegate(KeyValuePair<string, int> kv) { return kv.Value >= 0; });
d.Filter(kv => kv.Value >= 0);// or as lambda
Do you want to remove the items from that dictionary, or are you happy to use a new dictionary without those items included?
var d = new Dictionary<int,int>();
var newDict = d.Where(entry => entry.Value >= 0).ToDictionary(entry => entry.Key, entry => entry.Value);
Easiest one:
Dictionary<long, long> dict...
Dictionary<long, long> result = dict.Were(x => x.Value >= 0).ToDictionary(x => x.Key, x => x.Value);
Or just loop over all in 'for' in reverse order and remove invalid ones.
I know you said you are not into Linq, but I could not contain myself with the following solution, plus it is still useful if you read the title of your question. This is probably the most elegant solution to your problem:
dictionary.Where(pair => pair.Value < 0)
.Select(pair => {
dictionary.Remove(pair.Key);
return pair.Key;
});

C# Dictionary Composition

Let's say I have an arbitray list of A
class A
{
string K {get;set;}
string V {get;set;}
}
...
List<A> theList = ...
Is there an easy way to compose a dictionary from theList? (something like the following)
Dictionary<string, string> dict = magic(x => x.K, x => x.V, theList)
I don't want to write the following code:
var d = new Dictionary<string, string>
foreach(var blah in theList)
d[blah.K] = blah.V
There's this: Enumerable.ToDictionary.
You use it like this:
Dictionary<string, string> dict = theList.ToDictionary(e => e.K, e => e.V);
If the list is an IEnumerable<A> then most definitely. You would use the ToDictionary extension method on the Enumerable class in the System.Linq namespace in .NET 3.5 and beyond like so:
Dictionary<string, string> d = theList.ToDictionary(a => a.K, a => a.V);
Which will give you a Dictionary where the key is the value in the K property, and the value is the value in the V property.
var dict = theList.Cast<A>().ToDictionary(a => a.K, a => a.V);
Dictionary<string, string> dict = theList.ToDictionary( x => x.K , x=> x.V);
Enumarable.ToDictionary<TSource,TKey> is what you are looking for:
theList.ToDictionary(x => x.K, x => x.V);
Dictionary<string,string> dict = new Dictionary<string,string>();
theList.ForEach( param => dict[param.K] = param.V );
A little shorter, but still basically a for-each loop. I like the ToDictionary() solution better.

Categories