"Grouping" dictionary by value - c#

I have a dictionary: Dictionary<int,int>. I want to get new dictionary where keys of original dictionary represent as List<int>. This is what I mean:
var prices = new Dictionary<int,int>();
The prices contain the following data:
1 100
2 200
3 100
4 300
I want to get the IList<Dictionary<int,List<int>>>:
int List<int>
100 1,3
200 2
300 4
How can I do this?

var prices = new Dictionary<int, int>();
prices.Add(1, 100);
prices.Add(2, 200);
prices.Add(3, 100);
prices.Add(4, 300);
Dictionary<int,List<int>> test =
prices.GroupBy(r=> r.Value)
.ToDictionary(t=> t.Key, t=> t.Select(r=> r.Key).ToList());

You can use GroupBy.
Dictionary<int,List<int>> groups =
prices.GroupBy(x => x.Value)
.ToDictionary(x => x.Key, x => x.Select(i => i.Key).ToList());

Here is my reply. When the dictionaries get large, you will likely find the GroupBy() extension methods less efficient than you would like, as they provide many guarantees that you don't need, such as retaining order.
public static class DictionaryExtensions
{
public static IDictionary<TValue,List<TKey>> Reverse<TKey,TValue>(this IDictionary<TKey,TValue> src)
{
var result = new Dictionary<TValue,List<TKey>>();
foreach (var pair in src)
{
List<TKey> keyList;
if (!result.TryGetValue(pair.Value, out keyList))
{
keyList = new List<TKey>();
result[pair.Value] = keyList;
}
keyList.Add(pair.Key);
}
return result;
}
}
And an example to use in LinqPad:
void Main()
{
var prices = new Dictionary<int, int>();
prices.Add(1, 100);
prices.Add(2, 200);
prices.Add(3, 100);
prices.Add(4, 300);
// Dump method is provided by LinqPad.
prices.Reverse().Dump();
}

You can use GroupBy followed by the Func<TSource, TKey>, Func<TSource, TElement> overload of Enumerable.ToDictionary:
var d = prices.GroupBy(x => x.Value).ToDictionary(x => x.Key, x => x.ToList());

You can use Lookup instead.
var prices = new Dictionary<int, int> { {1, 100}, { 2, 200 }, { 3, 100 }, { 4, 300 } };
ILookup<int, int> groups = prices.ToLookup(x => x.Value, y => y.Key);
foreach (var group in groups)
{
foreach (var item in group)
{
Console.WriteLine(item);
}
}

In particular case, when we use the .NET framework 2.0, we can do as follows:
var prices = new Dictionary<int, int>();
prices.Add(1, 100);
prices.Add(2, 200);
prices.Add(3, 100);
prices.Add(4, 300);
Dictionary<int, List<int>> grouping = new Dictionary<int, List<int>>();
var enumerator = prices.GetEnumerator();
while (enumerator.MoveNext())
{
var pair = enumerator.Current;
if (!grouping.ContainsKey(pair.Value))
grouping[pair.Value] = new List<int>();
grouping[pair.Value].Add(pair.Key);
}

Related

How to use LINQ to find a sum?

I have this structure:
private readonly Dictionary<string, Dictionary<string, int>> _storage =
new Dictionary<string, Dictionary<string, int>>();
key: Firmware(string): key: Device(string) : value CountOfUsers (int)
I need to get the total of users for each device, but I really don't know how to do it with LINQ. Already tried a lot of variants. Please, help!
For now, I just use a whole function for it
private XlsRow2 GetTotalPerDevice(Dictionary<string, Dictionary<string, int>> storage)
{
XlsRow2 totalPerDeviceRow = new XlsRow2();
totalPerDeviceRow._Name = "Grand Total";
totalPerDeviceRow.UseBorders = true;
foreach (var deviceModel in _allDeviceModels)
{
foreach (var firmware in storage)
{
foreach (var device in firmware.Value)
{
var countOfUsers = 0;
if (deviceModel == device.Key)
{
countOfUsers += device.Value;
if (!_totalsPerDevice.ContainsKey(deviceModel))
{
_totalsPerDevice.Add(deviceModel, countOfUsers);
}
else
{
_totalsPerDevice[deviceModel] += countOfUsers;
}
}
}
}
}
foreach (var deviceModel in _allDeviceModels)
{
if (_totalsPerDevice.ContainsKey(deviceModel))
{
totalPerDeviceRow._AddColumn(_totalsPerDevice.First(k => k.Key == deviceModel.ToString()).Value.ToString());
}
else
{
totalPerDeviceRow._AddColumn("");
}
}
return totalPerDeviceRow;
}
Something like this for example?
var result = _storage.SelectMany(x => x.Value)
.GroupBy(x => x.Key)
.Select(x => new { Device = x.Key, Total = x.Sum(y => y.Value) });
Since the keys for the data that you would like to aggregate is in the second-level dictionary, a good first step would be to dump all key-value pairs from inner dictionaries into a flat sequence. After that all you need is to aggregate the counts, like this:
var res = _storage
.SelectMany(d => d.Value)
.GroupBy(kvp => kvp.Key)
.ToDictionary(g => g.Key, g => g.Sum(kvp => kvp.Value));
A Dictionary implements IEnumerable<KeyValuePair<TKey,TValue> which means you can use LINQ on it. In this case you have a dictionary of dictionaries and need to group by the second level key. To do that, you need to flatten the dictionaries, something that can be done with SelectMany
_storage.Selectmany(pair=>pair.Value);
Once you have the leaf-level entries, you can group by their keys:
_storage.Selectmany(pair=>pair.Value)
.GroupBy(leaf=>leaf.Key);
And calculate the sum per group:
var totals=_storage.SelectMany(pair=>pair.Value)
.GroupBy(leaf=>leaf.Key)
.Select(grp=>new {
Device = grp.Key,
TotalUsers =grp.Sum(leaf=>leaf.Value)
});
The equivalent query is rather cleaner:
var totals2 = from frm in _storage
from dev in frm.Value
group dev by dev.Key into grp
select new {
Device = grp.Key,
Total=grp.Sum(leaf=>leaf.Value)
};
Given the following dictionary:
var _storage = new Dictionary<string, Dictionary<string, int>> {
["Frm1"]=new Dictionary<string, int> {
["Device1"]=4,
["Device2"]=5
},
["Frm2"]=new Dictionary<string, int> {
["Device1"]=41,
["Device3"]=5
}
};
Both queries return the same values
foreach(var total in totals)
{
Console.WriteLine ($"{total.Device} = {total.Total}");
}
------------------
Device1 = 45
Device2 = 5
Device3 = 5
You can do this like:
Dictionary<string, Dictionary<string, int>> _storage = new Dictionary<string, Dictionary<string, int>>();
Dictionary<string, int> x = new Dictionary<string, int>();
x.Add("x", 2);
x.Add("z", 2);
x.Add("y", 2);
_storage.Add("x", x);
_storage.Add("z", x);
_storage.Add("y", x);
var b = _storage.SelectMany(keyValuePair => keyValuePair.Value)
.GroupBy(keyValuePair => keyValuePair.Key)
.ToDictionary(valuePairs => valuePairs.Key, grouping => grouping.Sum(kvp => kvp.Value));
result will be like:

c# collection type, to keep index order

I have a file with rows
keys values
-----+---------------------------
1 1 2 0.39785 0.39785 0.2043 36
1 1 3 0.409604 0.409604 0.180792 24
1 1 4 0.407281 0.407281 0.185438 24
1 1 5 0.404958 0.404958 0.190084 24
1 1 6 0.403399 0.403399 0.193203 24
...
23 34 36 0.414457 0.354921 0.230622 576
..
-the first 3 numbers are keys and represent a matchup, they are unique, and they are ascending
-the float values are linked to the keys. eg: first row's 4th element (0.39785) belongs to key 1, 6th element (0.2043) to 2.
I read it line by line and split it by " " (space).
How should I store it (which collection/structure).
Lets say I want to lookup "2 1 1".
As I wrote keys are ascending, there won't be an entry like "2 1 1",
only "1 1 2", so first I have to sort it, but I want to get the values
in the lookup's order (0.2043 0.39785 0.39785).
The data-structure below should meet all your requirements:
Dictionary<HashSet<int>, Dictionary<int, double>>
It should be easy to create an instance of the above structure with LINQ from your original data.
Access should be easy:
from 2, 1, 1 create a HashSet (2, 1)
lookup (2, 1) in the Dictionary -> ((1, 0.39785), (2, 0.2043))
with a partial key lookup a double like 2 -> 0.2043
CAVEAT The solution will only work as long as for identical int-values on one line the double-values are identical as well. (Which seems to hold true for the provided sample-data).
EDIT The code to create yourLookup:
List<List<int>> intList = new List<List<int>>() {
new List<int> () {1, 1, 2},
new List<int> () {1, 1, 3},
...
};
List<List<double>> doubleList = new List<List<double>> {
new List<double>() {0.39785, 0.39785, 0.2043},
new List<double>() {0.409604, 0.409604, 0.180792},
....
};
var dictionaries = intList.Zip(doubleList, (Is, Ds) =>
{ return Is.Zip(Ds, (i, d) => new KeyValuePair<int, double>(i, d)).Distinct()
.ToDictionary(kv => kv.Key, kv => kv.Value); });
var yourLookup = dictionaries.Select(
dictionary => new { hashset = new HashSet<int>(dictionary.Keys), dictionary })
.ToDictionary(x => x.hashset, x => x.dictionary);
Interesting problem. I created this class:
class Mapper
{
public void Add(int n1, int n2, int n3, double f1, double f2, double f3)
{
int[] intArray = new int[] {n1,n2, n3};
Array.Sort(intArray);
Dictionary<int, double> dict = new Dictionary<int, double>();
dict[n1] = f1;
dict[n2] = f2;
dict[n3] = f3;
myDictionary[string.Join("_", intArray.Select(i=>i.ToString()))] = dict;
}
public Tuple<double, double, double> Find(int n1, int n2, int n3)
{
string key = CreateKey(n1, n2, n3);
if (!myDictionary.ContainsKey(key))
return null;
Dictionary<int, double> found = myDictionary[key];
return new Tuple<double, double, double>(found[n1], found[n2], found[n3]);
}
private string CreateKey(int n1, int n2, int n3)
{
int[] intArray = new int[] { n1, n2, n3 };
Array.Sort(intArray);
return string.Join("_", intArray.Select(i => i.ToString()));
}
private Dictionary<string, Dictionary<int, double>> myDictionary = new Dictionary<string, Dictionary<int, double>>();
}
By adding the 6-tuple in Add it sorts the integers and joins them to the unique key string (e.g. 1_1_2). The doubles are inserted in a lookup dictionary. Some keys may be set here multiple times but as they the int->double associations are identical in one line it doesn't matter.
Accessing in Find happens in a corresponding way.
[TestMethod]
public void test()
{
var data = new string[]{
"1 1 2 0.39785 0.39785 0.2043 36",
"1 1 3 0.409604 0.409604 0.180792 24",
"1 1 4 0.407281 0.407281 0.185438 24",
"1 1 5 0.404958 0.404958 0.190084 24",
"1 1 6 0.403399 0.403399 0.193203 24"
};
var dic = new FloatLookup(data);
var test1 = dic.GetValues(211).ToArray();
CollectionAssert.AreEquivalent(new float[] { 0.39785F, 0.39785F, 0.2043F }, test1);
var test2 = dic.GetValues(121).ToArray();
CollectionAssert.AreEquivalent(new float[] { 0.39785F, 0.2043F, 0.39785F }, test2);
var test3 = dic.GetValues(611).ToArray();
CollectionAssert.AreEquivalent(new float[] { 0.193203F, 0.403399F, 0.403399F }, test3);
}
class FloatLookup
{
Dictionary<int, KeyValuePair<int, float>[]> dic;
public FloatLookup(string[] data)
{
dic = data.Select(GetKeyValuePair).
ToDictionary(o => o.Key, o => o.Value);
}
public IEnumerable<float> GetValues(int num)
{
return GetValues(GetInts(num));
}
public IEnumerable<float> GetValues(IEnumerable<int> ints)
{
var key = GetKey(ints);
KeyValuePair<int, float>[] kvps = null;
if (!dic.TryGetValue(key, out kvps))
yield break;
foreach (var i in ints)
yield return kvps.First(o => o.Key == i).Value;
}
static KeyValuePair<int, KeyValuePair<int, float>[]> GetKeyValuePair(string line)
{
var items = line.Split(' ');
var ints = new string[] { items[0], items[1], items[2] }.
Select(o => int.Parse(o)).ToArray();
var floats = new string[] { items[3], items[4], items[5] }.
Select(o => float.Parse(o)).ToArray();
var kvps = Enumerable.Range(0, 3).Select(o =>
new KeyValuePair<int, float>(ints[o], floats[o])).Distinct().ToArray();
var key = GetKey(ints);
return new KeyValuePair<int, KeyValuePair<int, float>[]>(key, kvps);
}
static int[] GetInts(int num)
{
return num.ToString().ToCharArray().Select(o =>
int.Parse(o.ToString())).ToArray();
}
static int GetKey(IEnumerable<int> ints)
{
var ret = 0;
var ary = ints.ToArray();
Array.Sort(ary);
var c = 1;
for (int i = ary.GetUpperBound(0); i > -1; i--)
{
ret += ary[i] * c;
c *= 10;
}
return ret;
}

Linq SelectMany project to index

I have 2 different collections.
pseudo code:
// index string by int : Dictionary<int, string>
index = { 0, "a" }, { 1, "b" }, { 2, "c" }
// data : Dictionary<string, List<Data>>
data = {"a", { "data00", "data01"..}},
{"b", {"data20", "data21", "data22"...}},
{"c", {"data4",...}}...
I want project int index to data string value and
var result = data.SelectMany ... new { IntIndex, DataValue }
I need to flatten lists into one sequence and pair the Data values with int index using string index.
I have slightly update types and values (your Dictionary contains duplicated keys and index wasn't specified) but it shouldn't be a problem. You may modify function easily for your data types.
var index = new List<Tuple<int, string>> {Tuple.Create(0, "a"), Tuple.Create(1, "b")};
var data = new Dictionary<string, IEnumerable<string>>()
{
{"a", new[] {"data00", "data01"}},
{"b", new[] {"data20", "data21", "data22"}},
{"c", new[] {"data4"}}
};
var result = index
.Join(data, x => x.Item2, y => y.Key, (x,y) => new KeyValuePair<int, IEnumerable<string>>(x.Item1, y.Value))
.SelectMany(x => x.Value, (x, y) => new KeyValuePair<int, string>(x.Key, y));
Assuming your duplication of keys was accidental, you could try this
Dictionary<int, List<Data>> intData = new Dictionary<int, List<Data>>();
foreach (var iVal in index)
{
List<Data> tmpList = new List<Data>();
if (data.TryGetValue(iVal.Value, out tmpList))
{
intData.Add(iVal.Key, tmpList);
}
}
If you can have duplicate keys then a dictionary is not the right structure.
var index = new List<Tuple<int, string>> {Tuple.Create(0, "a"), Tuple.Create(1, "b")};
var data = new Dictionary<string, IEnumerable<string>>()
{
{"a", new[] {"data00", "data01"}},
{"b", new[] {"data20", "data21", "data22"}},
{"c", new[] {"data4"}}
};
var res =
(from i in index
join d in data on i.Item2 equals d.Key
select new {Key = i.Item1, Value = d.Value})
.SelectMany(x => x.Value, (x, v) => new {x.Key, Value = v});

create a dictionary using 2 lists using LINQ

I am trying to create a dictionary from 2 lists where one list contains keys and one list contains values. I can do it using for loop but I am trying to find if there is a way of doing it using LINQ.
Sample code will be helpfull. Thanks!!!!
In .NET4 you could use the built-in Zip method to merge the two sequences, followed by a ToDictionary call:
var keys = new List<int> { 1, 2, 3 };
var values = new List<string> { "one", "two", "three" };
var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v })
.ToDictionary(x => x.Key, x => x.Value);
List<string> keys = new List<string>();
List<string> values = new List<string>();
Dictionary<string, string> dict = keys.ToDictionary(x => x, x => values[keys.IndexOf(x)]);
This of course assumes that the length of each list is the same and that the keys are unique.
UPDATE: This answer is far more efficient and should be used for lists of non-trivial size.
You can include the index in a Select expression to make this efficient:
var a = new List<string>() { "A", "B", "C" };
var b = new List<string>() { "1", "2", "3" };
var c = a.Select((x, i) => new {key = x, value = b[i]}).ToDictionary(e => e.key, e => e.value );
foreach (var d in c)
Console.WriteLine(d.Key + " = " + d.Value);
Console.ReadKey();
var dic = keys.Zip(values, (k, v) => new { k, v })
.ToDictionary(x => x.k, x => x.v);
You can use this code and working perfectly.
C# Code:
var keys = new List<string> { "Kalu", "Kishan", "Gourav" };
var values = new List<string> { "Singh", "Paneri", "Jain" };
Dictionary<string, string> dictionary = new Dictionary<string, string>();
for (int i = 0; i < keys.Count; i++)
{
dictionary.Add(keys[i].ToString(), values[i].ToString());
}
foreach (var data in dictionary)
{
Console.WriteLine("{0} {1}", data.Key, data.Value);
}
Console.ReadLine();
Output Screen:

Merging sequences by type With LINQ

I want to use LINQ to convert this
IEnumerable<int>[] value1ByType = new IEnumerable<int>[3];
value1ByType[0]= new [] { 0};
value1ByType[1]= new [] {10,11};
value1ByType[2]= new [] {20};
var value2ToType = new Dictionary<int,int> {
{100,0},
{101,1},
{102,2},
{103,1}};
to this
var value2ToValue1 = new Dictionary<int,int> {
{100, 0},
{101,10},
{102,20},
{103,11}};
Is there a way to do this with LINQ? Without LINQ I would use multiple IEnumerators, one for each IEnumerable of value1ByType. like this:
// create enumerators
var value1TypeEnumerators = new List<IEnumerator<int>>();
for (int i = 0; i < value1ByType.Length; i++)
{
value1TypeEnumerators.Add(value1ByType[i].GetEnumerator());
value1TypeEnumerators[i].MoveNext();
}
// create wanted dictionary
var value2ToValue1 = new Dictionary<int, int>();
foreach (var item in Value2ToType)
{
int value1=value1TypeEnumerators[item.Value].Current;
value2ToValue1.Add(item.Key, value1);
value1TypeEnumerators[item.Value].MoveNext();
}
Any Idea how to do this in LINQ?
Not pure but you can at least do ...
var enumerators = value1ByType.Select(v => v.GetEnumerator()).ToArray();
var value2ToValue1 = value2ToType
.ToDictionary(x => x.Key, x => { enumerators[x.Value].MoveNext(); return enumerators[x.Value].Current; });
But there are so many ways this could go wrong it begs the question - why was the data in those data-structures anyway? and can you fix that instead? How did you end up with exactly the right number of references in the 2nd data structure to elements in the first?
I'm pretty sure that #Hightechrider's solution is most performant than this one, but if you really like the syntax sugar way, you can do it like this:
public IDictionary<int, int> MergeSequences(IEnumerable<int>[] value1ByType, Dictionary<int, int> value2ToType)
{
int pos = 0;
var value1ByTypePos = from byType in value1ByType
select new { Pos = pos++, Enumerator = byType.GetEnumerator() };
return (from byType in value1ByTypePos
join toType in value2ToType
on byType.Pos equals toType.Value
select new { toType.Key, Value = byType.Enumerator.GetNext() })
.ToDictionary(pair => pair.Key, pair => pair.Value);
}
I've added an extension method to the IEnumerator interface like this:
public static T GetNext<T>(this IEnumerator<T> enumerator)
{
if (!enumerator.MoveNext())
throw new InvalidOperationException();
return enumerator.Current;
}
Now you have to be aware that any of this solutions can give you slightly different results, depending on how elements in the dictionary are enumerated. For example, another valid result to this code is:
var value2ToValue1 = new Dictionary<int,int> {
{100, 0},
{103, 10},
{102, 20},
{101, 11}};
Notice that now 101 is paired with 11 and 103 is paired with 10. If this is a problem, then you should use a SortedDictionary<int, int> when defining value2ToType variable.
What you can do for sure is replace the first part with the following:
var value1TypeEnumerators = value1ByType.ToList();
instead of using an enumerator.
If I do not care about performance I could also write:
var value2Ordered = Value2ToType.OrderBy(x => x.Value).Select(x=>x.Key);
var value1Ordered = from item in value1ByType from subitem in item select subitem;
var value2ToValue1 = value2Ordered.Zip(value1Ordered, (x, y) => new { Key = x, Value = y })
.ToDictionary(item => item.Key, item => item.Value);
I used the zip method from a stackoverflow community wiki. I didn't test this with the c#4.0 zip method

Categories