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
Related
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));
I have duplicate keys with different values and I want to convert it to a dictionary with 1 key and its values.
The next example will explain best what I mean:
var tup = new List<Tuple<int, int>>();
tup.Add(new Tuple<int, int>(1, 1));
tup.Add(new Tuple<int, int>(1, 2));
var dic = new Dictionary<int, List<int>>();
What is an elegant way to convert the tup to dic?
I managed to do this with foreach but would like to write it in LINQ.
foreach (var item in tup)
{
if (dic.ContainsKey(item.Item1))
{
dic[item.Item1].Add(item.Item2);
}
else
{
dic.Add(item.Item1, new List<int> { item.Item2 });
}
}
var list = tup.GroupBy(x => x.Item1)
.ToDictionary(
x => x.Key,
x => x.Select(y => y.Item2).ToList());
First, we group by GroupBy item 1. This should be obvious enough.
Then, we call ToDictionary and pass in a keySelector and an elementSelector. They select the key and value respectively, given an IGrouping<int, Tuple<int, int>>.
For reference, this particular overload of ToDictionary is used.
Alternatively, as Iridium has said in the comments, this works as well:
var list = tup.GroupBy(x => x.Item1, x => x.Item2)
.ToDictionary(x => x.Key, x => x.ToList());
This overload of GroupBy allows you to select 2 things!
You first need to group by the first tuple element in order to find all elements that have the same key in the dictionary. And then just collect the second tuple elements and make a list out of it:
tup.GroupBy(t => t.Item1)
.ToDictionary(g => g.Key, g => g.Select(t => t.Item2).ToList());
You can use GroupBy to resolve this problem, like:
var tup = new List<Tuple<int, int>>();
tup.Add(new Tuple<int, int>(1, 1));
tup.Add(new Tuple<int, int>(1, 2));
var dic = tup
.GroupBy(x => x.Item1)
.ToDictionary(x => x.Key, tuples => tuples.Select(x => x.Item2).ToList());
BTW, in some cases you can use NameValueCollection, but this is not save your target type, for example
var nvc = tup.Aggregate(new NameValueCollection(),
(seed, current) =>
{
seed.Add(current.Item1.ToString(), current.Item2.ToString());
return seed;
});
foreach (var item in nvc)
{
Console.WriteLine($"Key = {item} Value = {nvc[item.ToString()]}");
}
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());
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);
I have two dictionaries:
Dictionary<DateTime, decimal> d1; and Dictionary<DateTime, decimal> d2;
I want two execute a linq query to select from d1 all elements where d2..ContainsKey(d1.key);
var results = d1.Where(x => d2.ContainsKey(x.Key)).Select(x => x.Value).ToList();
Or create another dictionary from selected data:
var results = d1.Where(x => d2.ContainsKey(x.Key)).ToDictionary(x => x.Key, x => x.Value);
If you want the decimal-values as result:
IEnumerable<decimal> result = d2.Keys.Intersect(d1.Keys).Select(k => d1[k]);
If you want a new dictionary from the intersection:
Dictionary<DateTime, decimal> result = d2.Keys.Intersect(d1.Keys)
.ToDictionary(k => k, k => d1[k]);
from rec in d1 where d2.ContainsKey(rec.key) select d1
Try this:
var values = d1.Where(kvp => d2.ContainsKey(kvp.Key)).Select(kvp => kvp.Value);
d1.Keys.Intersect(d2.Keys).ToDictionary( x => x, x => d1[x]);
or
d1.Keys.Where(k => d2.ContainsKey(k)).ToDictionary( x => x, x => d1[x]);