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:
Related
I have a list I want to map to a Dictionary<string, object>. The resulting dictionary should look like:
var dictionary = new Dictionary<string, object>{
{ Prop.Name, item.Name },
{ Prop.Address, item.Address}
//other properties
}
I got stuck trying to leverage the Linq query below:
var dict = myList.Select((h, i) => new { key = h, index = i })
.ToDictionary(o => o.key, o => values[o.index]));
And...
foreach(var item in list){
var _dict = new Dictionary<string, object>{
{Prop.Name, item.Name},
//other properties
}
}
How can I map myList and return a dictionary like the one above? Is there a better way?
From your examples I see that you want map each item of list to Dictionary. Please try this code:
var commonProps = typeof(CommonProperty).GetProperties(BindingFlags.Static | BindingFlags.Public);
var itemProps = list.GetType().GenericTypeArguments[0].GetProperties().ToDictionary(k => k.Name, v => v);
var result = list.Select(l => commonProps.ToDictionary(
k => k.GetValue(null),
v => itemProps[v.Name].GetValue(l)
));
What exactly do you mean by "Both key and value are of the same 'names'"? If keys you want in the dicrionary match property names in an item then you can use reflection as follows:
item.GetType().GetProperties().ToDictionary(x => x.Name, x => x.GetValue(item));
This example does not filter by CommonProperty which can results in extra entries in the dictionary if item has any properties you are not interested in.
The following is the complete example program which displays properties of all files in a directory:
static class Program
{
static Dictionary<string, object> ObjToDic(object o)
{
return o.GetType().GetProperties().ToDictionary(x => x.Name, x => x.GetValue(o));
}
static void Main(string[] args)
{
var fileNames = Directory.EnumerateFiles("c:\\windows");
foreach (string name in fileNames)
{
Console.WriteLine("==========================================");
FileInfo fi = new FileInfo(name);
var propDict = ObjToDic(fi); // <== Here we convert FileInfo to dictionary
foreach (var item in propDict.AsEnumerable())
{
Console.WriteLine(string.Format("{0}: {1}", item.Key, item.Value.ToString()));
}
}
}
}
Bear in mind that in .NET there are properties and fields. Both are read and written using the same syntax in C#, but reflection handles them differently. The example above displays only properties.
I have a dictionary object like this:
CustomKeys<int, string>
eg;
1000, F1
1001, F2
1002, F1
1003, F4
1004, F2
I want to know if I have more than 1 of same values in this dictionary. I would also want to keep a note of which keys(unique id) has duplicates.
Is that possible?
It is possible using GroupBy and than Count() > 1 to keep track of which values that have duplicates.
var q = dic.GroupBy(x => x.Value)
.Select (x => new { Item = x, HasDuplicates = x.Count() > 1 });
You can find all key values they had the same values like this;
Dictionary<int, string> d = new Dictionary<int, string>();
d.Add(1000, "F1");
d.Add(1001, "F2");
d.Add(1002, "F1");
d.Add(1003, "F4");
d.Add(1004, "F2");
var dublicate = d.ToLookup(x => x.Value, x => x.Key).Where(x => x.Count() > 1);
foreach (var i in dublicate)
{
Console.WriteLine(i.Key);
}
Here is a DEMO.
But if you want to get a boolean value since your item's has a same value, look at Magnus's answer which is great.
I'm not sure by what you mean by "keeping note of which has duplicate values". If you mean keeping note of the keys, you could do this:
var keys = new Dictionary<int, string>();
keys.Add(1000, "F1");
keys.Add(1001, "F2");
keys.Add(1002, "F1");
keys.Add(1003, "F4");
keys.Add(1004, "F2");
var duplicates = keys.GroupBy(i => i.Value).Select(i => new
{
keys = i.Select(x => x.Key),
value = i.Key,
count = i.Count()
});
foreach (var duplicate in duplicates)
{
Console.WriteLine("Value: {0} Count: {1}", duplicate.value, duplicate.count);
foreach (var key in duplicate.keys)
{
Console.WriteLine(" - {0}", key);
}
}
If you mean keeping track of the duplicate values only, see Sonor's answer.
Another solution could be:
var duplicates = dictionary.GroupBy( g => g.Value )
.Where( x => x.Count( ) > 1 )
.Select( x => new { Item = x.First( ), Count = x.Count( ) } )
.ToList( );
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);
}
I have the following string pattern: 1:2,2:3.
This is like array in one string:
The first element is: 1:2
The second element is: 2:3
I want to parse it and create a dictionary:
1,2 // 0 element in Dictionary
2,3 // 1 element in Dictionary
This is my code:
Dictionary<int,int> placesTypes = new Dictionary<int, int>();
foreach (var place in places.Split(','))
{
var keyValuePair = place.Split(':');
placesTypes.Add(int.Parse(keyValuePair[0]), int.Parse(keyValuePair[1]));
}
Is there the best way to do this?
Thanks.
You could change it to this:
var d = s.Split(',')
.Select(x => x.Split(':'))
.ToDictionary(x => int.Parse(x[0]), x => int.Parse(x[1]));
Dictionary<int, int> dict = "1:2,2:3".Split(',')
.Select(x => x.Split(':'))
.ToDictionary(x => int.Parse(x[0]),
x => int.Parse(x[1]));
var result = input.Split(new[]{','})
.Select(s => s.Split(new[]{':'}))
.ToDictionary(k => int.Parse(k[0]), v=> int.Parse(v[1]));
Live example: http://rextester.com/GTKO60478
If you're using C# >= 3.5 the ToDictionary LINQ method is the way to go:
var dictionary = places.Split(',')
.Select(place => place.Split(':'))
.ToDictionary(keyValue => int.Parse(keyValue[0]), keyValue => int.Parse(keyValue[1]));
Failing that, the following should suffice:
public static Dictionary<string, string> ToDictionary(string value, char pairSeperator, char valueSeperator)
{
Dictionary<int, int> dictionary = new Dictionary<int, int>();
foreach (string pair in value.Split(pairSeperator))
{
string[] keyValue = pair.Split(valueSeperator);
dictionary.Add(keyValue[0], keyValue[1]);
}
return dictionary;
}
Splitting only once, using MoreLinq.Batch
Dictionary<int, int> dict = places.Split(',', ':').Batch(2).Select(x=>x.ToArray()).ToDictionary(x=>int.Parse(x[0]),x=>int.Parse(x[1]));
I have a List of String like
List<String> MyList=new List<String>{"A","B"};
and a
Dictionary<String, Dictionary<String,String>> MyDict=new Dictionary<String,Dictionary<String,String>>();
which contains
Key Value
Key Value
"ONE" "A_1" "1"
"A_2" "2"
"X_1" "3"
"X_2" "4"
"B_1" "5"
"TWO" "Y_1" "1"
"B_9" "2"
"A_4" "3"
"B_2" "6"
"X_3" "7"
I need to merge the the list and Dictionary into a new Dictionary
Dictionary<String,String> ResultDict = new Dictionary<String,String>()
The resulting dictionary contains
Key Value
"A_1" "1"
"A_2" "2"
"B_1" "5"
"A_4" "3"
"B_2" "6"
"X_2" "4"
"X_3" "7"
Merge rule
First add the items which has a substring equals to any item in the list.
Then Merge the items in the "MyDict" so the result should not contain duplicate keys as well as duplicate values.
Here is my source code.
Dictionary<String, String> ResultDict = new Dictionary<string, string>();
List<String> TempList = new List<string>(MyDict.Keys);
for (int i = 0; i < TempList.Count; i++)
{
ResultDict = ResultDict.Concat(MyDict[TempList[i]])
.Where(TEMP => MyList.Contains(TEMP.Key.Contains('_') == true ? TEMP.Key.Substring(0, TEMP.Key.LastIndexOf('_'))
: TEMP.Key.Trim()))
.ToLookup(TEMP => TEMP.Key, TEMP => TEMP.Value)
.ToDictionary(TEMP => TEMP.Key, TEMP => TEMP.First())
.GroupBy(pair => pair.Value)
.Select(group => group.First())
.ToDictionary(pair => pair.Key, pair => pair.Value); }
for (int i = 0; i < TempList.Count; i++)
{
ResultDict = ResultDict.Concat(MyDict[TempList[i]])
.ToLookup(TEMP => TEMP.Key, TEMP => TEMP.Value)
.ToDictionary(TEMP => TEMP.Key, TEMP => TEMP.First())
.GroupBy(pair => pair.Value)
.Select(group => group.First())
.ToDictionary(pair => pair.Key, pair => pair.Value);
}
its working fine, but I need to eliminate the two for loops or at least one
(Any way to do this using LINQ or LAMBDA expression)
Here's one way you could do it with LINQ and lambdas, as requested:
var keysFromList = new HashSet<string>(MyList);
var results =
MyDict.Values
.SelectMany(x => x)
.OrderBy(x => {
int i = x.Key.LastIndexOf('_');
string k = (i < 0) ? x.Key.Trim()
: x.Key.Substring(0, i);
return keysFromList.Contains(k) ? 0 : 1;
})
.Aggregate(new {
Results = new Dictionary<string, string>(),
Values = new HashSet<string>()
},
(a, x) => {
if (!a.Results.ContainsKey(x.Key)
&& !a.Values.Contains(x.Value))
{
a.Results.Add(x.Key, x.Value);
a.Values.Add(x.Value);
}
return a;
},
a => a.Results);
Loop wise this code is simpler, but not Linq:
public static Dictionary<string, string> Test()
{
int initcount = _myDict.Sum(keyValuePair => keyValuePair.Value.Count);
var usedValues = new Dictionary<string, string>(initcount); //reverse val/key
var result = new Dictionary<string, string>(initcount);
foreach (KeyValuePair<string, Dictionary<string, string>> internalDicts in _myDict)
{
foreach (KeyValuePair<string, string> valuePair in internalDicts.Value)
{
bool add = false;
if (KeyInList(_myList, valuePair.Key))
{
string removeKey;
if (usedValues.TryGetValue(valuePair.Value, out removeKey))
{
if (KeyInList(_myList, removeKey)) continue;
result.Remove(removeKey);
}
usedValues.Remove(valuePair.Value);
add = true;
}
if (!add && usedValues.ContainsKey(valuePair.Value)) continue;
result[valuePair.Key] = valuePair.Value;
usedValues[valuePair.Value] = valuePair.Key;
}
}
return result;
}
private static bool KeyInList(List<string> myList, string subKey)
{
string key = subKey.Substring(0, subKey.LastIndexOf('_'));
return myList.Contains(key);
}