代码之家  ›  专栏  ›  技术社区  ›  Pavel Foltyn

如何在C中生成逆字典#

  •  0
  • Pavel Foltyn  · 技术社区  · 6 月前

    让我们有一本电话簿:

    • “约翰”--->6463
    • “安德烈”--->1234
    • “迈克尔”--->2611
    • “艾琳”--->6463
    • “马修”--->7699

    请注意,约翰和艾琳共用同一个电话号码。

    我们想得到一个反向电话簿,将号码映射到 姓名:

    • 1234--->[“安德烈”]
    • 2611--->[“迈克尔”]
    • 6463--->[“约翰”、“艾琳”]
    • 7699--->[“马修”]

    现在,我用C#编写了以下泛型方法来生成“逆”字典:

    private static IDictionary<T, IList<U>> ToInverseDictionary<T, U>(IDictionary<U, T> dictionary)
    {
        var inverseDictionary = new Dictionary<T, IList<U>>();
    
        foreach (var objU in dictionary.Keys)
        {
            var objT = dictionary[objU];
    
            if (inverseDictionary.TryGetValue(objT, out var objsUExisting))
            {
                objsUExisting.Add(objU);
            }
            else
            {
                var objsU = new List<U>();
                objsU.Add(objU);
                inverseDictionary[objT] = objsU;
            }
        }
    
        return inverseDictionary;
    }
    

    然后我被建议使用LINQ。所以我想到了这个:

    private static IDictionary<T, IList<U>> ToInverseDictionary<T, U>(IDictionary<U, T> dictionary)
    {
        return dictionary.GroupBy(kvp => kvp.Value).ToDictionary(grouping => grouping.Key, grouping => grouping.Select(g => g.Key).ToList());
    }
    

    我的问题是:后一个代码片段是否会产生与前一个相同的结果?

    我不太确定,因为有“嵌套的λ”,我有点迷失在代码中。

    2 回复  |  直到 6 月前
        1
  •  3
  •   gunr2171    6 月前

    让我们对第二种方法进行一些快速格式化,以帮助解释这段代码的作用。

    (唯一的代码更改是生成的字典的值是 List IList ).

    private static IDictionary<T, List<U>> ToInverseDictionary<T, U>(IDictionary<U, T> dictionary)
    {
        return dictionary
            .GroupBy(kvp => kvp.Value)
            .ToDictionary(
                grouping => grouping.Key, 
                grouping => grouping
                    .Select(g => g.Key)
                    .ToList()
            );
    }
    

    一次只写一行:

    dictionary -这是我们的输入对象 <U, T> 最终,我们正在寻找一本类型的词典 <T, U> .

    .GroupBy(kvp => kvp.Value) -想象一下,你有一堆彩色大理石。这种方法通过一些属性来移动它们,我们希望 颜色 每一颗大理石。这会产生一组组,每个组都有两个属性: 钥匙 (你分组的内容:颜色),以及该组中的大理石列表。

    在这段代码中,您正在检查每个KeyValuePair( kvp )在输入字典中,根据值(电话号码)进行分组。这意味着你最终会得到4个分组,因为这是 不同的 你开头的电话号码。每个分组都包含由其组成的KeyValuePair条目,在您的情况下,这些条目可以是1个或2个。

    如果我们停下来看看结果 .GroupBy 我们会看到这样的东西:

    组密钥 集团价值观
    6463 KeyValuePair<string, int>("John", 6463) , KeyValuePair<string, int>("Irene", 6463)
    1234 KeyValuePair<string, int>("Andrea", 1234)
    2611 KeyValuePair<string, int>("Michael", 2611)
    7699 KeyValuePair<string, int>("Matthew", 7699)

    这已经开始看起来很像你想要的,但它只是不是一个 Dictionary 然而。

    .ToDictionary( -是时候构建结果字典了。该方法有两个参数,一个函数用于定义 Key ,以及定义 Value ,对于源集合中的每个对象。

    grouping => grouping.Key, - grouping 正如你所料,它代表了每个在 .GroupBy() . grouping.Key 是a T 类型以及如何确定每个组( => kvp.Value 在里面 .GroupBy ).

    grouping => grouping -以确定 价值观 在生成的字典中,我们首先查看组中所有条目的列表。。。

    .Select(g => g.Key) -对于该组中的每个条目,请填写 钥匙 键值对 (不要与密钥属性混淆 IGrouping ). KeyValuePair的Key属性是指 原始输入 字典,是人们的名字。你最终只会得到一组名单中的人名 分组 集团。

    .ToList() - .Select() 将产生 IEnumerable<string> ,我们想把它列成一个清单。

    你最终会成为 List<string> ,这是该组中所有人的名字。


    如果我不得不推测,我会说这里的大部分困惑在于你正在使用 KeyValuePair , I分组 ,以及 词典 _同时,它们 钥匙 价值观 属性,很容易混淆你认为某物属于哪种对象类型。

        2
  •  1
  •   Pavel Foltyn    6 月前

    我试图回答上述问题如下:

    为了反转字典,我们需要执行以下操作:

    1. 确定将成为逆字典键的(原始)字典值集。
    2. 从由上一步的值划分的原始字典中创建键组。稍后,我们将从每个组中生成一个元素列表(原始键),以便将其作为逆字典中的值。
    3. 通过合并前两个步骤中的信息来生成新的(反向)字典。

    有条件 问题中的LINQ表达式执行了这3个步骤,LINQ重写是正确的,完全替换了我开始使用的“丑陋”的20行方法。

    还有一个问题:

    在这种情况下,LINQ压缩值得做吗?这可能会使方法的目的变得模糊,而不是照亮它。