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
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.
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.)
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
).
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
// 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With