【问题标题】:How do I combine the keys and values of a Dictionary into one List using LINQ?如何使用 LINQ 将字典的键和值组合到一个列表中?
【发布时间】:2011-12-22 03:32:19
【问题描述】:

我有一个字典,其中键是字符串,值是对应于该键的字符串列表。我想显示字典中的所有键,并在该键下方显示与该键关联的值。像这样的:

Key 1
    Value 1
    Value 2
    Value 3
Key 2
    Value 1
    Value 2

在 C# 2.0 中,我会这样做(valuesDictionary):

StringBuilder sb = new StringBuilder();
foreach(KeyValuePair<string, List<string>> pair in values)
{
    sb.AppendLine(pair.Key);
    foreach(string item in pair.Value)
    {
        sb.AppendLine('\t' + item);
    }
}

我将如何使用 LINQ 进行等效操作?似乎应该可以,但是我不知道该怎么做。

如果我使用values.SelectMany(p =&gt; p.Values),那么最终结果中只有值,而不是键。

我想到的任何其他解决方案都有类似的限制。

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    C# 中的解决方案

    这是使用aggregate extension method的解决方案:

    string result = values.Aggregate("",
                      (keyString, pair) =>
                        keyString + "\n" + pair.Key + ":"
                          + pair.Value.Aggregate("",
                              (str, val) => str + "\n\t" + val)
                      );
    

    C# 中的聚合子句没有 LINQ 语法(但显然 Visual Basic 中有一些预定义函数)。

    此解决方案可能看起来有些复杂,但聚合方法非常有用。

    聚合的工作原理

    它的工作原理是这样的:如果您有一个List&lt;int&gt;,您可以将所有值组合成一个单个聚合值

    这与Select 方法相反,它不会修改列表的长度。或者 Where 方法,它确实缩小了列表,但它仍然是一个列表(而不是单个值)。

    例如,如果您有一个列表{1, 2, 3, 4},您可以将它们组合成一个值,如下所示:

    int[] xs = {1, 2, 3, 4};
    int sum = xs.Aggregate(0, (sumSoFar, x) => sumSoFar + x);
    

    所以你给Aggregate 方法提供了两个值;一个种子值和一个“组合器”函数:

    • 您从单个种子值开始聚合(在本例中为0)。
    • 您的组合器函数会为列表中的每个值调用。
      第一个参数是到目前为止的计算结果(第一次为零,以后会有其他值),并将其与值 x 结合。

    简而言之,Aggregate 方法在List&lt;int&gt; 上的工作原理。它在 KeyValuePair&lt;string, List&lt;string&gt;&gt; 上的工作方式相同,但类型不同。

    【讨论】:

    • +1 用于聪明的聚合解决方案,尽管第一个示例是否返回一个聚合(在这个词的真正含义中)如第二个示例,如果它实际上是一个更好的解决方案,那么foreach 上的简单头像:)
    • 关于“聚合”名称;我不太喜欢它,也许是因为它在英语中有特定的含义。在其他编程语言中,这个函数有时被称为:reduce、fold 或消除。
    【解决方案2】:

    我不确定你会如何在 LINQ 中做到这一点,但使用 lambdas 你可以执行以下操作:

    string foo = string.Join(Environment.NewLine, 
        values.Select(k => k.Key + Environment.NewLine + 
            string.Join(Environment.NewLine, 
                k.Value.Select(v => "\t" + v).ToArray())).ToArray());
    

    不过,这不是很可读。

    【讨论】:

      【解决方案3】:

      在 LINQ 中没有任何东西可以帮助您,因为您对值的要求与对键的要求不同(您在值之间使用 tab 键)。

      即使不是这种情况,LINQ 最多也只能帮助您获得一个循环遍历的枚举源,无论如何您都必须有某种分区逻辑来指示您何时是处理一组值与一个键。

      归结为,您已经拥有的 Dictionary 为您提供了一个自然分组,除非您处理分组以外的操作(排序、投影、过滤),否则 LINQ 将无法再提供帮助。

      【讨论】:

        【解决方案4】:

        如果你有这样的方法:

        public IEnumerable<string> GetStrings
          (KeyValuePair<string, List<string>> kvp)
        {
          List<string> result = new List<string>();
          result.Add(kvp.Key);
          result.AddRange(kvp.Value.Select(s => "\t" + s));
          return result;
        }
        

        那么你可以这样做:

        List<string> result = theDictionary
          .SelectMany(kvp => GetStrings(kvp)).ToList();
        

        或泛指:

        public static IEnumerable<T> GetFlattened<T, U>
          ( this KeyValuePair<T, List<U>> kvp,
            Func<U, T> ValueTransform
          )
        {
          List<T> result = new List<T>();
          result.Add(kvp.Key);
          result.AddRange(kvp.Value.Select(v => ValueTransform(v)));
          return result;
        }
        
        List<string> result = theDictionary
          .SelectMany(kvp => kvp.GetFlattened(v => "\t" + v))
          .ToList();
        

        【讨论】:

        • 这并不能回答问题,不过……听起来他想要一根弦
        • 我回答了一个有趣的问题,即如何展平字典。转换生成的 IEnumerable 几乎不需要想象 - string.Join 非常适合此目的。
        • 与他的原始代码相比,看起来只是几行代码,但实际上并没有做它做的事情:)
        • 是的,他的两个 foreach 循环很好地解决了特定的“问题”。有时,为了学习如何使用一种技术或技巧,必须编写非最优代码。
        【解决方案5】:

        我会对值进行选择,以逐项将内容放入初始列表中(使用 string.Join 作为值),然后将其弹出到字符串中(再次使用 string.Join)。

        IEnumerable<string> items = values.Select(v => 
            string.Format("{0}{1}{2}", v.Key, Environment.NewLine + "\t", 
            string.Join(Environment.NewLine + "\t", v.Value.ToArray()));
        string itemList = string.Join(Environment.NewLine, items.ToArray());
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-06-24
          • 1970-01-01
          • 2019-06-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-04-17
          相关资源
          最近更新 更多