23

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.

Community
  • 1
  • 1
roundcrisis
  • 17,276
  • 14
  • 60
  • 92
  • @Pins if an answer solves your problem satisfactorily, I recommend clicking the "checkmark" next to it to accept it as the official answer and reward the person who gave it. – Rex M Aug 24 '09 at 23:54
  • Answered in http://stackoverflow.com/questions/255341/ – Dour High Arch Aug 24 '09 at 23:55

3 Answers3

48

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
  • 37,201
  • 11
  • 119
  • 156
  • 3
    The problem with this is that you have to ensure that the Values are unique – Dan McClain Aug 24 '09 at 23:44
  • 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 Aug 24 '09 at 23:47
  • I just tried that and the result is a Dictionary, int> I m afraid that is not what I m looking for. Thanks for the answer tho – roundcrisis Aug 24 '09 at 23:48
  • 3
    By giving the most logical answer to the thread title, you completely failed to answer the content of the question inside. Interesting. – Sam Harwell Aug 25 '09 at 00:02
  • Take it easy, guys! I was editing it since when I saw the contents of the question! – Bruno Reis Aug 25 '09 at 00:08
8

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
  • 44,864
  • 6
  • 88
  • 112
3

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());
Community
  • 1
  • 1
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • 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 :) – roundcrisis Aug 24 '09 at 23:50
  • You can also do the following, which gives an `ILookup`: var classToStudent = studentToClass .SelectMany( pair => pair.Value.Select(val => new { Key = val, Value = pair.Key })) .ToLookup(item => item.Key, item => item.Value); – Sam Harwell Aug 25 '09 at 00:00