簡體   English   中英

如何使用LINQ將Dictionary的鍵和值組合到一個List中?

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

我有一個字典,其中鍵是一個字符串,值是與該鍵對應的字符串列表。 我想顯示字典中的所有鍵,其中與該鍵相關聯的值標簽在該鍵下方。 像這樣的東西:

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 => p.Values) ,那么只有值將在最終結果中,而不是鍵。

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

C#中的解決方案

以下是使用聚合擴展方法的解決方案:

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

C#中的聚合子句沒有LINQ語法(但顯然在Visual Basic中有一些預定義的函數)。

這個解決方案可能看起來有點復雜,但聚合方法非常有用。

Aggregate的工作原理

它的工作原理如下:如果你有一個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>>上的工作方式相同,但只是使用不同的類型。

我不確定你是怎么在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());

但這並不是非常易讀。

LINQ中沒有任何內容可以幫助您,因為您對值的要求與對鍵的要求不同(您在值之間切換)。

即使情況並非如此,充其量只是LINQ會幫助你只需要一個枚舉源來循環,你必須要有一些分區邏輯,以指示你何時處理一個集合價值與關鍵。

它歸結為你已經為你提供了一個自然分組,除非你正在處理除分組(排序,投影,過濾)以外的操作,否則LINQ將無法再提供幫助。

如果您有這樣的方法:

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

我會對值進行選擇,將項目放在初始列表中(使用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());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM