我有一个字典,其中键是一个字符串,值是与该键对应的字符串列表。我想显示字典中的所有键,其中与该键相关联的值标签在该键下方。像这样:
Key 1
Value 1
Value 2
Value 3
Key 2
Value 1
Value 2
在C#2.0中,我会这样做(values
是Dictionary
):
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 => p.Values)
,那么只有值会出现在最终结果中,而不是键。
我想到的任何其他解决方案都有类似的限制。
答案 0 :(得分:20)
以下是使用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<int>
,则可以将所有值合并为单个汇总值。
这与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<int>
上的工作原理。它在KeyValuePair<string, List<string>>
上的工作方式相同,但只是使用不同的类型。
答案 1 :(得分: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());
但这并不是非常易读。
答案 2 :(得分:1)
LINQ中没有任何内容可以帮助你,因为你对这些值有一套单独的要求,而不是你在键之间的选项(你在值之间选项卡)。
即使情况并非如此,充其量只是LINQ会帮助你只需要一个枚举源来循环,你必须要有一些分区逻辑来指示你什么时候处理一组值与一个键。
它归结为你已经为你提供了一个自然分组,除非你正在处理除分组(排序,投影,过滤)以外的操作,否则LINQ将无法再提供帮助。
答案 3 :(得分:1)
如果你有这样的方法:
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();
答案 4 :(得分:0)
我会对值进行选择,将项目放在初始列表中(使用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());