tags:

views:

1038

answers:

4

Actually my previous question got me thinking and I realized that reversing a Dictionary is not trivial. What is the most elegant and readable way to do it?

Same scenario student Many to Many with Classes

original Dicitonary<int, List<int>> where the key is studentId and the Value is a List<int> that contains classId and want to revert to Dictionary<classId, List<studentId>>

Thanks

Update: Actually I just tested Luke and Bruno's solutions and they return the right amount of classed whoever they all have the same student, will update as i go along.

+1  A: 

To reverse a dictionary is very easy:

var newDic = oldDic.ToDictionary(x => x.Value, x => x.Key);

That's all.

Now, your question is different. It is about reversing a many-to-many relationship, established on a dictionary.

So, let's say you have Dictionary<TEntity1, IEnumerable<TEntity2>>. The idea is to extract from this the "middle table" of the many-to-many relationship. Then you can regroup it by the other side, and retransform into a dictionary.

For the first part, we will use the overload of SelectMany that

"Projects each element of a sequence to an IEnumerable<T>, flattens the resulting sequences into one sequence, and invokes a result selector function on each element therein."

var table =
    dict.SelectMany(
        x => x.Value,
        (dictEntry, entryElement) => new
               {
                      Entity1 = dictEntry.Key,
                      Entity2 = entryElement
               }
    );

So, now you just have to regroup this table the way you want, and then convert it to a dictionary.

 var newDict =
     table
         .GroupBy(x => x.Entity2,
                  x => x.Entity1,
                  (entity2, entity1) => new {entity1, entity2})
         .ToDictionary(x => x.entity2, x => x.entity1);
Bruno Reis
The problem with this is that you have to ensure that the Values are unique
phsr
This is not correct. Read the contents of the question - it does not make sense to key off a list of class IDs for a single student ID.
Rex M
I just tried that and the result is a Dictionary<List<int>, int> I m afraid that is not what I m looking for. Thanks for the answer tho
Miau
Peer pressure badge!
MusiGenesis
By giving the most logical answer to the thread title, you completely failed to answer the content of the question inside. Interesting.
280Z28
Take it easy, guys! I was editing it since when I saw the contents of the question!
Bruno Reis
Thanks Bruno, detailed insight
Miau
+2  A: 

I'm not sure exactly how this differs from your previous question.

If you're just asking how to return a Dictionary<int, List<int>> rather than Dictionary<int, IEnumerable<int>> then all you need is a call to the ToList method.

Stealing and amending Mehrdad's answer to your other question:

var classToStudent = studentToClass
    .SelectMany(
        pair => pair.Value.Select(val => new { Key = val, Value = pair.Key }))
    .GroupBy(item => item.Key)
    .ToDictionary(gr => gr.Key, gr => gr.Select(item => item.Value).ToList());
LukeH
Thanks Luke, I was trying to find a simpler solution :D , I find that very cool tho hard to read. I cant understand how to get to this type of solution myself, so I was trying to simplify it. You are spot on tho. it does answer my question :)
Miau
You can also do the following, which gives an `ILookup<int, int>`: var classToStudent = studentToClass .SelectMany( pair => pair.Value.Select(val => new { Key = val, Value = pair.Key })) .ToLookup(item => item.Key, item => item.Value);
280Z28
+1  A: 

Slightly different way (a bit more comprehensible to my brain anyway :) ...

var newDict = new Dictionary<int, List<int>>();
var dict = new Dictionary<int, List<int>>();
dict.Add( 1, new List<int>() { 1, 2, 3, 4, 5 } );
dict.Add( 2, new List<int>() { 1, 2, 3, 4, 5 } );
dict.Add( 3, new List<int>() { 1, 2, 6 } );
dict.Add( 4, new List<int>() { 1, 6, 7 } );
dict.Add( 5, new List<int>() { 8 } );

var newKeys = dict.Values.SelectMany( v => v ).Distinct();

foreach( var nk in newKeys )
{
   var vals = dict.Keys.Where( k => dict[k].Contains(nk) );
   newDict.Add( nk, vals.ToList() );
}
JP Alioto
it works, its easy to understand and extremly elegant Thanks
Miau
+2  A: 

i say go with the solution that you best understand. what you don't want to be is a code-monkey, someone getting code solutions wherein you don't fully understand. take steps at a time, no programmer got it right the first time.

Martin Ongtangco