簡體   English   中英

C# LINQ 來自另一個列表的 OrderBy 值(IEnumerable)

[英]C# LINQ OrderBy values from another List(IEnumerable)

假設我們有一個字符串列表var listA = new List<string> {"B", "C", "A"} ,並且它以所需的方式排序。 並且,有一個 object 定義如下:

public class Sample {
   public string Letter {get;set;} // "A","B","C","D","#"
   public string Number {get;set;} // not relevant
}

此外,還有另一個列表List<Sample> listB ,我想對它進行排序,首先是字母值為“B”的對象,然后是“C”,最后是“A”,所以尊重相同按照 listA 中的順序排列。 有任何想法嗎? :)

編輯:字母屬性可以有更廣泛的字符,不像 listA 只能有那些 3. 預期順序“B”,“C”,“A”......“所有 rest,順序不相關”

假設Letter的值在listA內。 如果假設不成立,則值得考慮重新設計算法。 並且它可能取決於超出范圍的預期訂單的要求。

   var sortedSamples = listB.OrderBy(x=>listA.IndexOf(x.Letter));

為了獲得更好的性能,將訂單緩存到字典中:

   var orders = new Dictionary<string, int>();
   for (int i = 0; i < listA.Count; i++)
   {
      orders.Add(listA[i], i);
   }
   var sortedSamples = listB.OrderBy(x=>orders[x.Letter]);

如果Letter的可能值范圍比listA ,那么最好像這樣構造orders

   foreach (string letter in listB.Select(x=>x.Letter))
   {
      orders.Add(letter, listA.IndexOf(letter));
   }

更新

如果letters的范圍超出listA ,並且由於 OP 希望 out 的位於末尾:

   var orders = new Dictionary<string, int>();
   for (int i = 0; i < listA.Count; i++)
   {
      orders.Add(listA[i], i);
   }
   int lastIndex = listA.Count;
   foreach (string letter in listB.Select(x=>x.Letter)
                                  .Distinct().Except(listA))
   {
      orders.Add(letter, lastIndex);
   }

這是@nannanas answer的方法體的具體實現,當listA不包含listB中存在的所有字母時,它也有效:

listB = listA.Union(listB.Select(entry => entry.Letter))
    .Join(listB,
         letter => letter,
         sample => sample.Letter,
         ( _, sample ) => sample)
    .ToList();

示例小提琴在這里


演練

假設我們有以下列表:

var listA = new List<string> { "B", "C", "A" };

var listB = new List<Sample>
{
    new Sample { Letter = "E", Number = "1" },
    new Sample { Letter = "C", Number = "2" },
    new Sample { Letter = "A", Number = "3" },
    new Sample { Letter = "B", Number = "4" },
    new Sample { Letter = "D", Number = "5" }
};

第一行:

listB.Select(entry => entry.Letter)將提取listB中的每個Letter值,導致:

{ "E", "C", "A", "B", "D" }

listA.Union(listB.Select(entry => entry.Letter))使用.Union()創建listAlistBLetter值的集合並集,方法是首先生成listA集合中的每個不同項目( "B", "C", "A" ), 然后從listB中產生Letter集合中的每個不同的項目,這些項目不存在於listA ( "E", "D" ) 中; 導致:

{“B”,“C”,“A”,“E”,“D”}

我們現在有了希望listB反映的字母順序:首先是listA給出的順序,然后是listB中出現的剩余字母。

.Join()操作

.Join()根據每個序列鍵選擇器定義的關聯鍵將外部序列內部序列相關聯; 然后根據結果選擇器從關聯中返回一個IEnumerable<T>

outerSequence.Join(innerSequence,
    outerItem => /*  */, // key selector (outerItem is a string)
    innerItem => /*  */, // key selector (innerItem is a Sample)
    ( associatedOuterItem, associatedInnerItem ) => /*  */) // result selector

{ "B", "C", "A", "E", "D" }因此是.Join()操作中的外部序列 .Join()操作的內部序列listB 通過使用.Join() ,我們可以通過序列的匹配鍵選擇器外部序列中的項目與內部序列中的項目相關聯。

對於外部序列,我們將鍵選擇器定義為整個項目:

letter => letter // alternatively: outerItem => outerItem

例如,外部序列中的前兩個項目是"B""C" 因此,外部序列中前兩項的關聯鍵是"B""C"

對於內部序列,我們將鍵選擇器定義為Sample object 的Letter屬性:

sample => sample.Letter // alternatively: innerItem => innerItem.Letter

例如,內部序列中的前兩個項目是Sample { Letter = "E", Number = "1" }Sample { Letter = "C", Number = "2" } ; 因此,內部序列中前兩個項目的關聯鍵是每個項目的Letter值: "E""C"

因此,外部內部序列的全套密鑰選擇器分別是:

{“B”,“C”,“A”,“E”,“D”}
{ "E", "C", "A", "B", "D" }

.Join()操作現在根據鍵選擇器關聯每個序列中的項目。 關聯保持外部序列提供的順序,並且可以說明如下:

來自外部序列的項目 來自內部序列的項目
"B" Sample { Letter = "B", Number = "4" }
"C" Sample { Letter = "C", Number = "2" }
"A" Sample { Letter = "A", Number = "3" }
"E" Sample { Letter = "E", Number = "1" }
"D" Sample { Letter = "D", Number = "5" }

最后一個.Join()輸入參數是結果選擇器,它定義了每個關聯應返回的內容。 鑒於我們只需要內部序列中的項目,我們在結果選擇器中指定:

( _, sample ) => sample

結果選擇器可以重寫為例如( letter, sample ) => sample ,但是由於外部序列(稱為letter )中的項目不感興趣,我只是簡單地丟棄了 ( _ ) 我的 object例子。)

你應該使用這樣的東西

            var listA = new List<string> { "B", "C", "A" };
            var listB = new List<string> { "A", "B", "C" };
            listB = listB.OrderBy(d => listA.IndexOf(d)).ToList();
            // listB: "B", "C", "A"

        

您可以使用此擴展,它還支持通過特定屬性(例如 id 而不是引用)比較列表中的條目

public static IEnumerable<TFirst> OrderBy<TFirst, TSecond, TKey>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TKey> firstKeySelector, Func<TSecond, TKey> secondKeySelector) =>
            second
                .Join(
                    first,
                    secondKeySelector,
                    firstKeySelector,
                    (_, firstItem) => firstItem);

這將在second項目中對項目進行排序,就像在first使用兩個鍵選擇器比較它們時已經對它們進行排序一樣。

請注意,這僅在first中的所有鍵也存在於second中時才有效。

暫無
暫無

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

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