Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reversing Dictionary using LINQ in C#

How to convert

Dictioanry<String,List<String>> into Dictionary<String,String>

i'm having a dictionary like

Dictioanry<String,List<String>>dictOne=new Dictionary<String,List<String>>();

and which containg

Key(String)          Value(List<String>)

     A                a1,a2
     B                b1,b2
     C                c1

i need to convert the "dictOne" into

 Dictionary<String,String> dictReverse=new Dictionary<String,String>()

So the result will be like

Key(String)         Value(String)

   a1                  A
   a2                  A
   b1                   B
   b2                  B
   c1                   C

is there any way to do this using LINQ

Thanks in advance

like image 667
Thorin Oakenshield Avatar asked Sep 13 '10 05:09

Thorin Oakenshield


2 Answers

Update: As others have noted, in order for a dictionary to be truly "reversible" in this way, the values in your List<string> objects need to all be unique; otherwise, you cannot create a Dictionary<string, string> with an entry for every value in your source dictionary, as there would be duplicate keys.

Example:

var dictOne = new Dictionary<string, List<string>>
{
    { "A", new List<string> { "a1", "a2" } },
    { "B", new List<string> { "b1", "b2" } },
    { "C", new List<string> { "c1", "a2" } } // duplicate!
};

You have (at least) two options for dealing with this.

Option 1: Throw on duplicates

You may want to ensure that every element in every List<string> is, in fact, unique. In this case, a simple SelectMany with a ToDictionary will accomplish what you need; the ToDictionary call will throw an ArgumentException on encountering a duplicate value:

var dictTwo = dictOne
    .SelectMany(kvp => kvp.Value.Select(s => new { Key = s, Value = kvp.Key }))
    .ToDictionary(x => x.Key, x => x.Value);

The most generic way (that comes to mind) to abstract this functionality into its own method would be to implement an extension method that does this for any IDictionary<T, TEnumerable> implementation where TEnumerable implements IEnumerable<TValue>:

// Code uglified to fit within horizonal scroll area
public static Dictionary<T2, T1> ReverseDictionary<T1, T2, TEnumerable>(
    this IDictionary<T1, TEnumerable> source) where TEnumerable : IEnumerable<T2>
{
    return source
        .SelectMany(e => e.Value.Select(s => new { Key = s, Value = e.Key }))
        .ToDictionary(x => x.Key, x => x.Value);
}

The ugly proliferation of generic type parameters in the above method is to allow for types other than strictly Dictionary<T, List<T>>: it could accept a Dictionary<int, string[]>, for example, or a SortedList<string, Queue<DateTime>> -- just a couple of arbitrary examples to demonstrate its flexibility.

(A test program illustrating this method is at the bottom of this answer.)

Option 2: Skip duplicates

If duplicate elements in your List<string> values is a realistic scenario that you want to be able to handle without throwing an exception, I suggest you take a look at Gabe's excellent answer for an approach that uses GroupBy (actually, Gabe also provides a flexible approach that can cover either of these two cases based on a selector function; however, if you definitely want to throw on a duplicate, I'd still suggest the above approach, as it should be somewhat cheaper than using GroupBy).

Example program

Here's a little test program illustrating Option 1 above on a Dictionary<string, List<string>> with no duplicate elements in its List<string> values:

var dictOne = new Dictionary<string, List<string>>
{
    { "A", new List<string> { "a1", "a2" } },
    { "B", new List<string> { "b1", "b2" } },
    { "C", new List<string> { "c1" } }
};

// Using ReverseDictionary implementation described above:
var dictTwo = dictOne.ReverseDictionary<string, string, List<string>>();

foreach (var entry in dictTwo)
{
    Console.WriteLine("{0}: {1}", entry.Key, entry.Value);
}

Output:

a1: A
a2: A
b1: B
b2: B
c1: C
like image 174
Dan Tao Avatar answered Oct 02 '22 05:10

Dan Tao


// Associates each key with each of its values. Produces a sequence like:
// {A, a1}, {A, a2}, {B, b1}, {B, b2}, {C, c1}            
var kvps = from kvp in dictOne
           from value in kvp.Value
           select new { Key = kvp.Key, Value = value };    

// Turns the sequence into a dictionary, with the old 'Value' as the new 'Key'
var dictReverse = kvps.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);

Of course, each key in the original dictionary must be associated with a unique set of values, and no key must be associated with values that are also associated with other keys.

Also bear in mind that Dictionary<K, V> does not define any sort of enumeration order. You can use the Enumerable.OrderBy method to enumerate the resulting dictionary in the appropriate order.

like image 21
Ani Avatar answered Oct 02 '22 05:10

Ani