代码之家  ›  专栏  ›  技术社区  ›  Thorin Oakenshield

在C语言中使用LINQ反转字典#

  •  5
  • Thorin Oakenshield  · 技术社区  · 14 年前

    如何转换

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

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

    Key(String)          Value(List<String>)
    
         A                a1,a2
         B                b1,b2
         C                c1
    

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

    所以结果会是

    Key(String)         Value(String)
    
       a1                  A
       a2                  A
       b1                   B
       b2                  B
       c1                   C
    

    3 回复  |  直到 14 年前
        1
  •  13
  •   Community CDub    8 年前

    :正如其他人所指出的,为了使一本词典以这种方式真正“可逆” 价值观 List<string> 所有对象都必须是唯一的;否则,无法创建 Dictionary<string, string> 源字典中的每个值都有一个条目,因为会有重复的键。

    例子:

    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!
    };
    

    选项1:抛出重复项

    列表<字符串> 实际上是独一无二的。在这种情况下,一个简单的 SelectMany ToDictionary 将完成你所需要的 ArgumentException

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

    将这个功能抽象到它自己的方法中的最通用的方法(想到这里)是实现一个扩展方法,该方法对任何 IDictionary<T, TEnumerable> 实施地点 TEnumerable 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);
    }
    

    Dictionary<T, List<T>> :它可以接受 Dictionary<int, string[]> SortedList<string, Queue<DateTime>> --只是几个任意的例子来证明它的灵活性。

    (说明此方法的测试程序位于答案底部。)

    选项2:跳过重复项

    如果您的 值是一个现实的场景,您希望能够在不引发异常的情况下处理它,我建议您看看 Gabe's excellent answer 对于使用 GroupBy 一定地 如果想要复制,我仍然建议使用上面的方法,因为它应该比使用更便宜一些 ).

    示例程序

    方案1 上面的 Dictionary<string, List<string>> 具有 在它的 列表<字符串> 价值观:

    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);
    }
    

    a1: A
    a2: A
    b1: B
    b2: B
    c1: C
    
        2
  •  9
  •   Ani    14 年前
    // 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);
    

    当然,原始字典中的每个键都必须与一组唯一的值相关联,并且任何键都不能与也与其他键相关联的值相关联。

    还要记住 Dictionary<K, V> 不定义任何类型的枚举顺序。你可以用 Enumerable.OrderBy 方法以按适当顺序枚举生成的字典。

        3
  •  8
  •   Gabe Timothy Khouri    14 年前

    First ):

    var dictReverse = (from kvp in dictOne
                       from value in kvp.Value
                       group kvp.Key by value)
                       .ToDictionary(grp => grp.Key, grp => grp.First());
    

    给定此输入字典:

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

    c1: C
    a2: C
    b1: B
    b2: B
    a1: A

    正如Dan指出的,对于重复的键,您可能需要不同的行为。您可以创建此函数:

    public static Dictionary<V, K> Transpose<K, V>(
        this Dictionary<K, IEnumerable<V>> dictOne,
        Func<IEnumerable<K>, K> selector)
    {
        return (from kvp in dictOne
                from V value in kvp.Value
                group kvp.Key by value)
                    .ToDictionary(grp => grp.Key, grp => selector(grp));
    }
    

    你可以这样称呼它 dictOne.Transpose(Enumerable.First) 为了得到上述行为, dictOne.Transpose(Enumerable.Single) 要在有重复键(其他帖子的行为)时获取异常, dictOne.Transpose(Enumerable.Min) 要按字典顺序选择第一个函数,或传入自己的函数,请执行任何需要的操作。