C# 如何使用 LINQ 将字典的键和值组合到一个列表中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/753321/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 23:50:37  来源:igfitidea点击:

How do I combine the keys and values of a Dictionary into one List using LINQ?

c#linq

提问by Andy

I have a dictionary, where the key is a string and the value is a list of strings that correspond to that key. I would like to display all of the keys in the dictionary, with the values associated with that key tabbed in underneath that key. Something like this:

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

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

In C# 2.0, I would do that like this (valuesis the Dictionary):

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

How would I do the equivalent using LINQ? It seems like it should be possible, however I can't figure out how to do it.

我将如何使用 LINQ 做等效的事情?看起来应该是可能的,但是我不知道该怎么做。

If I use values.SelectMany(p => p.Values), then only the values will be in the final result, not the keys as well.

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

Any other solution that I've thought of has a similar limitation.

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

采纳答案by Tom Lokhorst

A Solution in C#

C#中的解决方案

Here is a solution using the aggregate extension method:

这是使用聚合扩展方法的解决方案:

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

There is no LINQ syntax for the aggregate clause in C# (but apparently there is in Visual Basicfor some predefined functions).

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

This solution might look somewhat complex, but the aggregate method is quite useful.

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

How Aggregate works

聚合的工作原理

It works like this: If you have a List<int>you can combine all values into a single aggregate value.

它是这样工作的:如果你有一个,List<int>你可以将所有值组合成一个聚合值

That is in contrast to the Selectmethod, which doesn't modify the length of the list. Or the Wheremethod, which does shrink the list, but it still remains a list (instead of a single value).

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

For example, if you have a list {1, 2, 3, 4}you can combine them into a single value like this:

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

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

So you give two values to the Aggregatemethod; a seed value and a 'combiner' function:

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

  • You start aggregating with a single seed value (0in this case).
  • Your combiner function gets called for each value in the list.
    The first argument is the computed result so far (zero the first time, later this will have other values), and it combines it with the value x.
  • 您开始聚合单个种子值(0在本例中)。
  • 为列表中的每个值调用组合器函数。
    第一个参数是到目前为止的计算结果(第一次为零,以后会有其他值),并将其与 value 组合x

That's in short how the Aggregatemethod works on List<int>. It works the same on KeyValuePair<string, List<string>>, but just with different types.

简而言之,这就是该Aggregate方法在List<int>. 它在 上工作相同KeyValuePair<string, List<string>>,但只是类型不同。

回答by Adam Robinson

I'm not sure how you'd do it in LINQ, but using lambdas you could do something like:

我不确定你会如何在 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());

That's not terribly readable, though.

不过,这不是非常可读。

回答by casperOne

There isn't anything in LINQ that is going to help you here, because you have a separate set of requirements for the values than you do the keys (you tab between the values).

LINQ 中没有任何内容可以帮助您,因为您对值有一组单独的要求而不是键(您在值之间切换)。

Even if that wasn't the case, at best, LINQ is going to help you with just getting a single enumeration source to cycle through, which you would have to have some sort of partitioning logic in anyways to indicate when you are processing a set of values versus a key.

即使情况并非如此,LINQ 充其量也将帮助您获得一个单一的枚举源来循环遍历,无论如何您都必须有某种分区逻辑来指示您何时处理一个集合值与键的关系。

What it comes down to is that the Dictionary you have already gives you a natural grouping which LINQ won't be able to help any more with unless you are dealing with an operation other than grouping (sorting, projection, filtering).

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

回答by Amy B

If you had a method like this:

如果你有这样的方法:

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

Then you could do this:

那么你可以这样做:

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

Or generically:

或者一般来说:

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

回答by Jacob Proffitt

I'd do a select on the values to put things in an initial list by item (using string.Join for the values) and then pop that into a string (again using string.Join).

我会对值进行选择,以逐项将内容放入初始列表中(使用 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());