Merge multiple dictionaries and aggregate values where required - c#

I have three Dictionaries created by calling ToDictionary on a GroupBy projection in LINQ.
var dictionaryOne = _repositoryOne.GetData()
.GroupBy(d => new { d.Property1, d.Property2, d.LocalCcyId})
.ToDictionary(d =>
new
{
d.Key.Property1,
d.Key.Property2,
d.Key.LocalCcyId
},
v => v.Sum(l => ConvertToUsd(effectiveDate, l.LocalCcyId, l.Amount)));
var dictionaryTwo = _repositoryTwo.GetData()
.GroupBy(d => new { d.Property1, d.Property2, d.LocalCcyId})
.ToDictionary(d =>
new
{
d.Key.Property1,
d.Key.Property2,
d.Key.LocalCcyId
},
v => v.Sum(l => ConvertToUsd(effectiveDate, l.LocalCcyId, l.Balance)));
var dictionaryThree = _repositoryThree.GetData()
.GroupBy(d => new { d.Property1, d.Property2, d.LocalCcyId})
.ToDictionary(d =>
new
{
d.Key.Property1,
d.Key.Property2,
d.Key.LocalCcyId
},
v => v.Sum(l => ConvertToUsd(effectiveDate, l.LocalCcyId, l.Total)));
I want to merge these into a dictionary and
i) Sum up the values which are in USD &
ii) Drop the grouping by LocalCcyId column from the Key
The will be instances to the same key occurring in each of the three dictionaries and I need to aggregate the Sums for all such cases. How do I achieve this in LINQ?

Seems to me that this is all you need:
var finalDictionary =
dictionaryOne
.Concat(dictionaryTwo)
.Concat(dictionaryThree)
.GroupBy(x => new { x.Key.Property1, x.Key.Property2 }, x => x.Value)
.ToDictionary(x => new { x.Key.Property1, x.Key.Property2 }, x => x.Sum());
Or, using LINQ syntax (as much as possible) this:
var finalDictionary =
(
from x in dictionaryOne.Concat(dictionaryTwo).Concat(dictionaryThree)
group x.Value by new { x.Key.Property1, x.Key.Property2 }
)
.ToDictionary(x => new { x.Key.Property1, x.Key.Property2 }, x => x.Sum());

Assuming you are querying a remote datasource, running queries twice over the data or convering to USD twice doesn't seem more efficient then taking the dictionaries and combining them, so that's what I did.
First you need to convert each Dictionary to a new anonymous object having the data you need, then group by the properties summing the values:
var allDictionary = dictionaryOne.Select(kv => new { kv.Key.Property1, kv.Key.Property2, kv.Value })
.Concat(dictionaryTwo.Select(kv => new { kv.Key.Property1, kv.Key.Property2, kv.Value }))
.Concat(dictionaryThree.Select(kv => new { kv.Key.Property1, kv.Key.Property2, kv.Value }))
.GroupBy(k2v => new { k2v.Property1, k2v.Property2 })
.ToDictionary(k2vg => new { k2vg.Key.Property1, k2vg.Key.Property2 }, k2vg => k2vg.Sum(k2v => k2v.Value));

Related

Linq - Get a list and sort it by a list of string values

I have a list of guids as string:
This is how i retrive my list of string guids:
List<string> p0 = ctx.PrProjectRating.Select(k => k).GroupBy(g => new { g.PrPIdG }, (key, group) => new { sumR = group.Sum(k => k.PrValue), pidG = key.PrPIdG }).Select(t => t.pidG).ToList();
Now i have another list that contains a field called pidG but this list needs to be ordered by the list of guid strings above.
How do i achiveve this.
i tried:
List<PProject> p = p.OrderBy(c => p0.Contains(c.PIdG)).ToList();
but still the list is not ordered by the string guids in the first list "p0"
You have to do join here
List<string> p0 = ctx.PrProjectRating
.Select(k => k)
.GroupBy(g => new { g.PrPIdG }, (key, group) =>
new { sumR = group.Sum(k => k.PrValue), pidG = key.PrPIdG })
.Select(t => t.pidG).ToList();
var result = p0.Join(p, x => x, c => c.PIdG, (x, c) => c)
.ToList()

Converting Tuple<List<Guid>, string> to Dictionary<Guid, List<string>>

I am trying to converting a Tuple<List<Guid>, string> to Dictionary<Guid, List<string>>. This is what I have so far:
var listOfTuples = GetListOfTuples(); // returns type List<Tuple<List<Guid>, string>>
var transformedDictionary = new Dictionary<Guid, List<string>>();
foreach (var listOfTuple in listOfTuples)
{
foreach (var key in listOfTuple.Item1)
{
if (!transformedDictionary.ContainsKey(key))
transformedDictionary[key] = new List<string> { listOfTuple.Item2 };
else transformedDictionary[key].Add(listOfTuple.Item2);
}
}
Is there a better way of doing this, perhaps using LINQ; SelectMany, Grouping, or toDictionary?
Update: I have tried this, but clearly not working:
listOfTuples.ToList()
.SelectMany(x => x.Item1,(y, z) => new { key = y.Item2, value = z })
.GroupBy(p => p.key)
.ToDictionary(x => x.Key, x => x.Select(m => m.key));
You are close. The problem is with selecting the right key and value
var result = listOfTuples.SelectMany(t => t.Item1.Select(g => (g, str: t.Item2)))
.GroupBy(item => item.g, item => item.str)
.ToDictionary(g => g.Key, g => g.ToList());
The mistake is here (y, z) => new { key = y.Item2, value = z } - you want the key to be the Guid and therefore instead of it being Item2 it should be z which is the Guid. So you can go with the way I wrote it or just
(y, z) => new { key = z, value = y.Item2 }
Also the .ToList() at the beginning is not needed. You say that listOfTuples already returns a list

Turning a Dictionary<Guid,IList<String>> into Dictionary<string,IList<Guid>> With LINQ?

I have a Dictionary<Guid,IList<string>> which shows all the names an entity can have.
I want to convert this to see all the names mapped to all the entities.
so:
[["FFF" => "a", "b"],
["EEE" => "a", "c"]]
Becomes
[["a" => "FFF", "EEE"],
["b" => "FFF"],
["c" => "EEE"]]
I know this is easy to do with foreaches but I'm wondering if there is a way with LINQ / ToDictionary?
private static void Main(string[] args)
{
var source = new Dictionary<Guid, IList<string>>
{
{ Guid.NewGuid(), new List<string> { "a", "b" } },
{ Guid.NewGuid(), new List<string> { "b", "c" } },
};
var result = source
.SelectMany(x => x.Value, (x, y) => new { Key = y, Value = x.Key })
.GroupBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.Select(y => y.Value).ToList());
foreach (var item in result)
{
Console.WriteLine($"Key: {item.Key}, Values: {string.Join(", ", item.Value)}");
}
}
var dic = new Dictionary<string, List<string>>()
{
{"FFF", new List<string>(){"a", "b"}},
{"EEE", new List<string>(){"a", "c"}}
};
var res = dic.SelectMany(x => x.Value, (x,y) => new{Key = y, Value = x.Key})
.ToLookup(x => x.Key, x => x.Value);
Dictionary<int,IList<string>> d = new Dictionary<int ,IList<string>>(){
{1,new string[]{"a","b"}},
{2,new string[]{"a","d"}},
{3,new string[]{"b","c"}},
{4,new string[]{"x","y"}}};
d.SelectMany(kvp => kvp.Value.Select(element => new { kvp.Key, element}))
.GroupBy(g => g.element, g => g.Key)
.ToDictionary(g => g.Key, g => g.ToList());

Comparing two lists from different tables with EntityFramework

I want to compare two lists extracted from two different tables
var maListe = db.exclure.Where(w => w.type.Contains("Object class"))
.GroupBy(g => new {libelle = g.libelle,})
.Select(s => new {libellex = s.Key.libelle}).ToList();
var myList = db.Full.Where(c => c.date_reception > new DateTime(2015, 02, 02))
.Where (c => !maListe.Any(c2 => c2.libellex.Contains(c.mc_object_class)))
//.Where (p => p.mc_object_class.CompareTo("NULL")<0)
.GroupBy(f => new
{
object_class = f.mc_object_class,
})
.Select(g => new
{
object_classx = g.Key.object_class,
countx = g.Count()
})
.Take(10)
.OrderByDescending(o => o.countx)
.ToList();
I'm looking for elements that exist in myList and not in maListe, while running the code above I get the following error:
Unable to create a constant value of type 'Anonymous type'. Only
primitive types or enumeration types are supported in this context.'
You should materialize your collection, something like:
var maListe = db.exclure.Where(w => w.type.Contains("Object class"))
.GroupBy(g => new {libelle = g.libelle,})
.Select(s => new {libellex = s.Key.libelle}).ToList();
var myList = db.Full.Where(c => c.date_reception > new DateTime(2015, 02, 02))
.AsEnumerable() // database query ends here
.Where (c => !maListe.Any(c2 => c2.libellex.Contains(c.mc_object_class)))
//.Where (p => p.mc_object_class.CompareTo("NULL")<0)
.GroupBy(f => new
{
object_class = f.mc_object_class,
})
.Select(g => new
{
object_classx = g.Key.object_class,
countx = g.Count()
})
.Take(10)
.OrderByDescending(o => o.countx)
.ToList();
If you will have time, please, take a look to msdn

Converting a LINQ query into a Dictionary<string, string[]>

I've got a query that returns something of the following format:
{ "tesla", "model s" }
{ "tesla", "roadster" }
{ "honda", "civic" }
{ "honda", "accord" }
and I'd like to convert that to a dictionary of <string, string[]> like so:
{ "tesla" : ["model s", "roadster"], "honda" : ["civic", "accord"] }
I've tried with this:
var result = query.Select(q => new { q.Manufacturer, q.Car}).Distinct().ToDictionary(q => q.Manufacturer.ToString(), q => q.Car.ToArray());
but so far I am not having any luck. I think what this is doing is actually trying to add individual items like "tesla" : ["model s"] and "tesla" : ["roadster"] and that's why it's failing ... any easy way to accomplish what I am trying to do in LINQ?
You would need to group each item by the key first, then construct the dictionary:
result = query.Select(q => new { q.Manufacturer, q.Car}).Distinct()
.GroupBy(q => q.Manufacturer)
.ToDictionary(g => g.Key,
g => g.Select(q => q.Car).ToArray());
Of course, an ILookup<string, string> much easier:
result = query.Select(q => new { q.Manufacturer, q.Car }).Distinct()
.ToLookup(q => q.Manufacturer, q => q.Car);
You're looking for ToLookup if you would like the results to be grouped into a dictionary-like object:
var result = query.Select(q => new { q.Manufacturer, q.Car})
.Distinct()
.ToLookup(q => q.Manufacturer.ToString(), q => q.Car);
Otherwise you will have to group the results first:
var result = query.Select(q => new { q.Manufacturer, q.Car })
.Distinct()
.GroupBy(q => q.Manufacturer)
.ToDictionary(gg => gg.Key,
gg => gg.Select(q => q.Car).ToArray());
What you want is GroupBy(), followed by ToDictionary().
Example:
var result = query.GroupBy(q => q.Manufacturer).ToDictionary(q => q.Key, q => q.Value.ToArray());
What GroupBy() does is group all the elements that have the same matching key selector. So when you tell it to GroupBy(q => q.Manufacturer), all the elements that have the same Manufacturer will be grouped together as IEnumerable<T>.
Use ToLookup:
var table = pairs.ToLookup(kvp => kvp.Key, kvp => kvp.Value);
foreach(var i in table["tesla"])
Console.WriteLine(i);

Categories

Resources