Grouping KeyValue pairs to Dictionary - c#

I have the following code :
using System.Collections.Generic;
public class Test
{
static void Main()
{
var items = new List<KeyValuePair<int, User>>
{
new KeyValuePair<int, User>(1, new User {FirstName = "Name1"}),
new KeyValuePair<int, User>(1, new User {FirstName = "Name2"}),
new KeyValuePair<int, User>(2, new User {FirstName = "Name3"}),
new KeyValuePair<int, User>(2, new User {FirstName = "Name4"})
};
}
}
public class User
{
public string FirstName { get; set; }
}
Above as you can see there are multiple users for same key . Now I want to group them and convert the list object to dictionary in which the Key will be the same(1,2 as shown above) but the value will be the collection.Like this:
var outputNeed = new Dictionary<int, Collection<User>>();
//Output:
//1,Collection<User>
//2,Collection<User>
i.e they are grouped now.
How can I achieve that ?

I would suggest you use a Lookup<TKey, TElement> instead. This data-structure is specifically intended for use as a map from keys to collections of values.
//uses Enumerable.ToLookup: the Id is the key, and the User object the value
var outputNeeded = items.ToLookup(kvp => kvp.Key, kvp => kvp.Value);
Of course, if you do need the dictionary (to allow for mutability perhaps), you can do something like:
var outputNeeded = new Dictionary<int, Collection<User>>();
foreach(var kvp in list)
{
Collection<User> userBucketForId;
if(!outputNeeded.TryGetValue(kvp.Key, out userBucketForId))
{
// bucket doesn't exist, create a new bucket for the Id, containing the user
outputNeeded.Add(kvp.Key, new Collection<User> { kvp.Value });
}
else
{ // bucket already exists, append user to it.
userBucketForId.Add(kvp.Value);
}
}
On another note, theCollection<T>class is not all that useful unless you intend to subclass it. Are you sure you don't just need aList<User>?

Here's an example using LINQ's ToDictionary:
var output = items.GroupBy(kvp => kvp.Key)
.ToDictionary(group => group.Key,
group => group.Select(kvp => kvp.Value).ToList());
It results in a Dictionary<int,List<User>>.

Given your initial variable "item", and the suggested output variable "outputNeed", this is what you need to do:
NOTE:This is not actual c#/vb code, so please change this pseudo as required (I don't have a VS Studio at hand at the moment):
foreach (KeyValuePair<int, User> pair in items)
{
//add a new collection if this is the first time you encounter the key
if (! outputNeed.Contains(pair.Key)
{
outputNeed[pair.Key] = new ArrayList<User>());
}
//add the user to the matching collection
outputNeed.Add(pair.Key, pair.User);
}
Good luck

foreach (var u in items)
{
if (outputNeed.Contains(u.Key)) {
outputNeed[u.Key].Add(u.Value);
}
else {
Collection<User> a=new Collection<User>();
a.Add(u.Value);
outputNeed.Add(u.Key,a);
}
}

Here is my solution :
var items = new List<KeyValuePair<int, User>>
{
new KeyValuePair<int, User>(1, new User { FirstName = "Name1" }),
new KeyValuePair<int, User>(1, new User { FirstName = "Name2" }),
new KeyValuePair<int, User>(2, new User { FirstName = "Name3" }),
new KeyValuePair<int, User>(2, new User { FirstName = "Name4" })
};
var result = (
from item in items
group item.Value by item.Key into grouped
select grouped
).ToDictionary(g => g.Key, g => g);
DotNetFiddle

Related

How to use dictionary in c# to compare two lists

Currently, I have implemented two lists with a double for loop to find matches between the two lists so I can join on them.
I have a list A which contains an ID and some other columns. I have a list B which contains an ID and some other columns. I have currently implemented a for loop within a for loop in order to make the comparisons for all the IDs so that I can find the ones that match and then return the joined results. I know want to understand how to implement a dictionary in this case as that will be more efficient to fix this problem.
public IEnumerable<Details> GetDetails(string ID)
{
// there are two lists defined up here
for (var item in listA)
{
for (var item2 in listB)
{
if (item.ID == item2.ID)
{
item.Name = item2.name;
}
}
}
return results;
}
Instead of having this double for loop, which is very inefficient. I want to learn how to implement a dictionary to fix this problem.
The dictionary would use the ids as keys (or indexes) so
Dictionary<string, object> myListA = new Dictionary<string, object>();
Dictionary<string, object> myListB = new Dictionary<string, object>();
public object GetDetails(string ID)
{
object a = myListA[ID];
object b = myListB[ID];
// combine them here how you want
// object c = a + b;
return c;
}
How about using linq to achieve your actual requirement? Something like:
public IEnumerable<A> GetDetails(int ID)
{
var listA = new List<A>
{
new A(){ ID = 1, Name = 2 },
new A(){ ID = 3, Name = 4 },
new A(){ ID = 5, Name = 6 },
};
var listB = new List<B>
{
new B(){ X = 1, name = 0 },
new B(){ X = 3, name = 1 }
};
return listA.Join(listB, k => k.ID, k => k.ID, (item, item2) =>
{
item.Name = item2.name;
return item;
}).Where(w => w.ID == ID);
}
If you just want the common IDs in the two lists, you can achieve that like this:
var commonIds = listA.Select(o => o.ID).Intersect(listB.Select(o => o.ID));

Is there any efficient way to optimize following Linq

private static List<Patient> GetPatientData()
{
return new List<Patient>()
{
new Patient(1,new List<Case>() { new Case(10, CaseType.ambulant)}),
new Patient(2,new List<Case>() { new Case(20, CaseType.ambulant), new Case(21, CaseType.ambulant), new Case(22, CaseType.stationaer),new Case(23, CaseType.teilstat) }),
new Patient(3,new List<Case>() { new Case(30, CaseType.ambulant), new Case(31, CaseType.ambulant), new Case(32, CaseType.stationaer), new Case(33, CaseType.stationaer), new Case(34, CaseType.teilstat) }),
new Patient(4,new List<Case>() { new Case(40, CaseType.ambulant), new Case(41, CaseType.stationaer), new Case(43, CaseType.teilstat), new Case(44, CaseType.ambulant), new Case(45, CaseType.stationaer), new Case(46, CaseType.teilstat) }),
new Patient(5,new List<Case>() {new Case(53, CaseType.teilstat),new Case(54, CaseType.teilstat) })
};
}
List<Patient> patientList = GetPatientData();
var result = patientList.SelectMany(item => item.CaseList.Select(itemCase=> itemCase.CaseType).Distinct());
foreach (CaseType item in result)
{
Console.WriteLine("CaseTypes = {0}",item);
}
The above code gives the list of Patient, from that I want a distinct case list. So is there any optimized way to do this from what I have done?
Dictionary<int, int> result = patientList.ToDictionary(item => item.PatientID , item=> item.CaseList.Select(x=>x.CaseType).Distinct().Count());
foreach (KeyValuePair<int,int> item in result)
{
Console.WriteLine("{0} {1}", item.Key,item.Value);
}
In Second case i am trying to get patientID & Distinct CaseType Count for that particular patient. can i optimize this one.
To get the distinct case types of all patients in that list, I used
var result =
(
from patient in patientList
from typ in patient.CaseList
select typ.CaseType
).Distinct();
foreach (var item in result)
{
Console.WriteLine("CaseTypes = {0}", item);
}
Of course, you could rewrite it method-style.
To get the same distinct case list, but per patient, try this:
var result =
(
from patient in patientList
group patient by patient.PatientID into g
from patient in g
from typ in patient.CaseList
select new { ID = g.Key, Type = typ.CaseType }
).Distinct();
foreach (var item in result)
{
Console.WriteLine("Patient {0} has the following case: {1}", item.ID, item.Type);
}
An alternative (less repeating) way to present the results is to group:
var result =
(
from patient in patientList
group patient by patient.PatientID into g
from p in g
select new { g.Key, List = p.CaseList.Select(c => c.CaseType).Distinct() }
).ToDictionary(kv => kv.Key, kv => kv.List);
foreach (var item in result)
{
Console.WriteLine("Patient {0} has the following cases:", item.Key);
foreach (var type in item.Value)
{
Console.WriteLine("\t{0}", type);
}
}
You are missing a last Distinct:
var result = patientList.SelectMany(item => item.CaseList.Select(itemCase => itemCase.CaseType).Distinct()).Distinct();
There's nothing to improve there.
You could choose to keep only the last Distinct (so there's one less Enumerator to be created) but having it gives you less results yielded out to your final Distinct. Performance then would change depending on the amount of data (number of Patients and Cases per Patient) though nothing that would make any difference.

How to get below format of nested dictionary?

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

how to filter dictionary values if its key exists in a string[] using linq

string[] strLst = { "One", "Three" };
Dictionary<string, Customer> dicDocTypes = new Dictionary<string, Customer>();
dicDocTypes.Add("One", new Customer { Id = 1, Name = "Rabi" });
dicDocTypes.Add("Two", new Customer { Id = 2, Name = "Shuvankar" });
dicDocTypes.Add("Three", new Customer { Id = 3, Name = "Krishna" });
dicDocTypes.Add("Four", new Customer { Id = 4, Name = "Suresh" });
var rootNodes = from node in dicDocTypes
where node.Key.Contains(**strLst**)
select new KeyValuePair<string, Customer>(node.Key, node.Value);
Question: how to filter dictionary items if the keys matches with a string[]
Instead of trying to ask the key if it belongs to an array, you can ask the array if it contains the key:
var rootNodes = from node in dicDocTypes
where strLst.Contains(node.Key)
select new KeyValuePair<string, Customer>(node.Key, node.Value);
If you have a lot of items, it will be faster to use a HashSet instead of an array (O(1) lookups instead of O(n) lookups):
var strLst = new HashSet<string> { "One", "Three" };
Also, you can simplify the select to this:
var rootNodes = dicDocTypes.Where(n => strLst.Contains(n.Key));
Additionally, there's a cleaner way to initialize dictionaries:
var dicDocTypes = new Dictionary<string, Customer> {
{ "One", new Customer { Id = 1, Name = "Rabi" } },
{ "Two", new Customer { Id = 2, Name = "Shuvankar" } },
{ "Three", new Customer { Id = 3, Name = "Krishna" } },
{ "Four", new Customer { Id = 4, Name = "Suresh" } }
};
As Cameron has said, you're just asking the question the wrong way round. However, you can also make your code simpler - the "node" in a dictionary is already a KeyValuePair, so all you need is:
var rootNodes = dicDocTypes.Where(pair => strLst.Contains(pair.Key));
If you need a new dictionary at the end, you can use the ToDictionary method:
var rootNodes = dicDocTypes.Where(pair => strLst.Contains(pair.Key))
.ToDictionary(pair => pair.Key, pair => pair.Value);
If your list of strings to filter by becomes longer, you might want to consider making it a HashSet<string> instead of an array.
var rootNodes = dicDocTypes.Join(strList, kvp => kvp.Key, s => s, (kvp, str) => kvp);

How to enumerate through an anonymously-typed collection?

This is what I have:
List<Person> list = new List<Person>()
{
new Person { Name="test", Age=1 },
new Person { Name="tester", Age=2 }
};
var items = list.Select(x =>
{
return new
{
Name = x.Name
};
});
foreach (object o in items)
{
Console.WriteLine(o.GetType().GetProperty("Name").GetValue(o, null));
}
I feel like I'm not doing it correctly.
Is there a simpler way to access properties of anonymous types in a collection?
Use the var keyword in the foreach line as well, instead of the general object type. The compiler will then automatically resolve the anonymous type and all its members, so you can access the properties directly by name.
foreach (var o in items)
{
Console.WriteLine(o.Name);
}
var items = list.Select(x =>new { Name = x.Name });
foreach (var o in items)
{
Console.WriteLine(o.Name);
}
Just use var and you'll have complete type support
Why not just this?
var items = list.Select(x => x.Name);
foreach (var o in items)
Console.WriteLine(o);
You're only getting a single field, no need to create another anonymous type.
The previous answers are sufficient enough.
Just about simplification, notice than you didn't reacj the maximal simplification possible.
You can write
list.ForEach(person => Console.WriteLine(person.Name));
or
list.Select(person => person.Name).ToList().ForEach(Console.WriteLine);
If you're going to iterate through the whole anonymous collection anyway, you could ToList() it and use the List.ForEach() method:
List<Person> list = new List<Person>()
{
new Person { Name="test", Age=1},
new Person { Name="tester", Age=2}
};
var items = list.Select(x =>
{
return new
{
Name = x.Name
};
}).ToList();
items.ForEach(o => Console.WriteLine(o.Name));

Categories