I have two dictionaries
One is nested dictionary -
Dictionary<string, List<string>> Dict = new Dictionary<string, List<string>>();
And another is normal one –
Dictionary<string, string> ObjDict = new Dictionary<string, string>();
In normal dictionary I have values like this
{[DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_1.xml, 0000000047510D9744C9A54EB11C0]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_2.xml, 0000000047510D9744C9A54EB11C0]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_1.xml, 0000000047510D9744C9A54EB11C1]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_2.xml, 0000000047510D9744C9A54EB11C1]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_3.xml, 0000000047510D9744C9A54EB11C2]}
Now I want the nested dictionary like this –
“Key0” DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_1.xml
DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_2.xml
“Key1” DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_1.xml
DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_2.xml
“Key2” DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_3.xml
All the keys of equal values of normal dictionary should belongs to one key of nested dictionary.
Please suggest.
ObjDict.GroupBy(kvp => kvp.Value).ToDictionary(g => g.Key, g => g.Select(kvp => kvp.Key).ToList())
Ok, if I understand the question correctly:
Given you have an input dictionary where you want to group the items by the values in the dictionary and put them into a new dictionary where each key is one of the values from the original dictionary, and each value is a list of the keys with that value:
var items = new Dictionary<string, string>
{
{"C03", "C"},
{"B01", "B"},
{"A01", "A"},
{"C02", "C"},
{"A03", "A"},
{"B03", "B"},
{"B02", "B"},
{"A02", "A"},
{"C01", "C"}
};
var result = items.GroupBy(item => item.Value)
.ToDictionary
(
g => g.Key,
g => g.Select(x => x.Key).ToList()
);
foreach (var item in result)
{
Console.Write("Values for key " + item.Key + ": ");
foreach (var value in item.Value)
Console.Write(value + " ");
Console.WriteLine();
}
The above code produces the following output:
Values for key C: C03 C02 C01
Values for key B: B01 B03 B02
Values for key A: A01 A03 A02
As you can see, this output is not ordered (it's in the same order as the input).
If you want to order both the keys in the dictionary, and the values in each list, you can do so like this:
var result = items.GroupBy(item => item.Value)
.OrderBy(item => item.Key)
.ToDictionary
(
g => g.Key,
g => g.Select(x => x.Key).OrderBy(x => x).ToList()
);
This change produces this output:
Values for key A: A01 A02 A03
Values for key B: B01 B02 B03
Values for key C: C01 C02 C03
you can use that:
foreach (var item in ObjDict)
{
if (Dict.ContainsKey(item.Value))
{
var e = Dict[item.Value];
e.Add(item.Key);
}
else
{
Dict.Add(item.Value, new List<string>() { item.Key });
}
}
you could create your own Dictionary, MultiDictionary,
in the Multipledictionary, maintain a private dictionary with key and List
when add the value, check if key has already exists, if exists, add to the list
and you can make it Generic
public class MultipleDictionary<TKey, TValue>
{
private IDictionary<TKey, IList<TValue>> _dict;
public bool ContainsKey(TKey key)
{
return _dict.ContainsKey(key);
}
public TValue this[TKey key]
{
get
{
if (_dict.ContainsKey(key))
return _dict[key][0];
return default(TValue);
}
set
{
if (_dict.ContainsKey(key))
{
IList<TValue> valList = _dict[key];
valList.Add(value);
}
else
{
IList<TValue> list = new List<TValue>();
list.Add(value);
_dict.Add(key, list);
}
}
}
}
this is the idea, try implement the rest yourself
A bit of pseudocode, so you can do some work on your own :P
step 1:
make a hashTable or something which stores your ObjDict.Values and your Dict.Keys
( "0000000047510D9744C9A54EB11C0" -> "Key0" )
step2:
for each ObjDict.Key
if(Dict.contains(HashTable[thisKey])
Take List from Dict and add the ObjDict.Value
else
Make new list, add ObjDict.Value
Dict.Add(HashTable[thiskey], theList
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 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:
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 can't have the same key. But a simple (and effective) solution is put a suffix after key.
But as I'm in a foreach, I was wondering a fast and clean way to add a number suffix to duplicated keys.
e.g.:
My foreach is that:
foreach (Item item in items) {
dic.Add(item.SomeKey, item.SomeValue);
}
But I don't want duplicated keys, so I need to 'handle' SomeKey to Origin become Result
SomeKey Origin: key, clave, clave, chave, chave, chave
SomeKey Result: key, clave, clave1, chave, chave1, chave2
Edit:
My answer to #KooKiz explain better the question.
I have few duplicated entries. I'm just trying to figure out how to then increment the suffix until you find no item. Sounds like reinvent wheels, so I was wondering if someone know a good way to do that
That may not be the fastest, but that's the more readable I can think of:
var source = new List<Tuple<string, string>>
{
new Tuple<string, string>("a", "a"),
new Tuple<string, string>("a", "b"),
new Tuple<string, string>("b", "c"),
new Tuple<string, string>("b", "d"),
};
var groups = source.GroupBy(t => t.Item1, t => t.Item2);
var result = new Dictionary<string, string>();
foreach (var group in groups)
{
int index = 0;
foreach (var value in group)
{
string key = group.Key;
if (index > 0)
{
key += index;
}
result.Add(key, value);
index++;
}
}
foreach (var kvp in result)
{
Console.WriteLine("{0} => {1}", kvp.Key, kvp.Value);
}
If you want a key with several "sub" items, Try this
Dictionary<string, List<string>> myList = new Dictionary<string, List<string>>();
foreach (Item item in items)
{
if (myList[item.SomeKey] == null)
myList.Add(item.SomeKey, new List<string>());
myList[item.SomeKey].Add(item.SomeValue);
}
var a = items.GroupBy(p => p.SomeKey)
.SelectMany(q => q.Select((value, index) =>
new Item { SomeKey = (q.Count() > 1 && index > 0) ? value.SomeKey + (index) :
value.SomeKey, SomeValue = value.SomeValue }))
.ToDictionary(p => p.SomeKey, q => q.SomeValue);
How can I change the value of a number of keys in a dictionary.
I have the following dictionary :
SortedDictionary<int,SortedDictionary<string,List<string>>>
I want to loop through this sorted dictionary and change the key to key+1 if the key value is greater than a certain amount.
As Jason said, you can't change the key of an existing dictionary entry. You'll have to remove/add using a new key like so:
// we need to cache the keys to update since we can't
// modify the collection during enumeration
var keysToUpdate = new List<int>();
foreach (var entry in dict)
{
if (entry.Key < MinKeyValue)
{
keysToUpdate.Add(entry.Key);
}
}
foreach (int keyToUpdate in keysToUpdate)
{
SortedDictionary<string, List<string>> value = dict[keyToUpdate];
int newKey = keyToUpdate + 1;
// increment the key until arriving at one that doesn't already exist
while (dict.ContainsKey(newKey))
{
newKey++;
}
dict.Remove(keyToUpdate);
dict.Add(newKey, value);
}
You need to remove the items and re-add them with their new key. Per MSDN:
Keys must be immutable as long as they are used as keys in the SortedDictionary(TKey, TValue).
You can use LINQ statment for it
var maxValue = 10
sd= sd.ToDictionary(d => d.key > maxValue ? d.key : d.Key +1, d=> d.Value);
If you don't mind recreating the dictionary, you could use a LINQ statment.
var dictionary = new SortedDictionary<int, SortedDictionary<string, List<string>>>();
var insertAt = 10;
var newValues = dictionary.ToDictionary(
x => x.Key < insertAt ? x.Key : x.Key + 1,
x => x.Value);
return new SortedDictionary<int, SortedDictionary<string, List<string>>>(newValues);
or
var dictionary = new SortedDictionary<int, SortedDictionary<string, List<string>>>();
var insertAt = 10;
var newValues = dictionary.ToDictionary(
x => x.Key < insertAt ? x.Key : x.Key + 1,
x => x.Value);
dictionary.Clear();
foreach(var item in newValues) dictionary.Add(item.Key, item.Value);