一千萬個為什麽

搜索

LINQ中與多個實例/ IEqualityComparer問題的等價比較


This is similar to my last question; but from a different angle. See if item exists once in Enumerable (Linq)

給定以下項目集,以及包含它們的列表......

Item 1
Item 2
Item 3
Item 4
Item 5

class Item
{
 string Name { get; set; }
}

List available = new List()
{
 Item 1
 Item 1
 Item 2
 Item 3
 Item 5
}

List selected = new List()
{
 Item 1
 Item 2
 Item 3
}

我需要創建一個包含“available”的第三個List,除了“selected”中的內容。 但是,“第1項”處於“可用”狀態兩次,但僅在“已選擇”一次。由於它們是同一項目的實例,因此我無法找出適當的邏輯來適應這一點。

最終的數組應該看起來像......

List selectable = new List()
{
 Item 1
 Item5
}

最佳答案

這是一種有點棘手的方法,但它完成了工作。我借用了Python中的“Decorate-Sort-Undecorate”習語,它通過將臨時排序鍵與數組相關聯來進行排序,並結合.NET中的匿名類型具有基於以下內容進行比較的默認EqualityComparer的有趣且有用的事實。他們的領域的價值觀。

步驟1:按名稱對每個列表中的元素進行分組,然後將索引與每個組中的每個項相關聯,並將這些組展平回常規列表:

var indexedAvailable = available.GroupBy(i => i.Name)
                                .SelectMany(g => g.Select((itm, idx) => new 
                                              { Name = itm.Name, Index = idx }));
var indexedSelected = selected.GroupBy(i => i.Name)
                              .SelectMany(g => g.Select((itm, idx) => new
                                              { Name = itm.Name, Index = idx }));

這將把列表變成這些:

indexedAvailable            indexedSelected
Name = Item 1, Index = 0    Name = Item 1, Index = 0
Name = Item 1, Index = 1    Name = Item 2, Index = 0
Name = Item 2, Index = 0    Name = Item 3, Index = 0
Name = Item 3, Index = 0
Name = Item 5, Index = 0

現在您可以看到,在索引列表中, available 中每個編號出現的任何名稱只會與等效編號的出現 selected 中的相同名稱。因此,你可以使用一個簡單的 Except 刪除 indexedAvailable 中不在 indexedSelected 中的任何內容,然後通過轉換匿名來“undecorate”鍵入索引對象回到 Item s。

var selectable = indexedAvailable.Except(indexedSelected)
                                 .Select(i => new Item() { Name = i.Name });

並證明:

foreach (var item in selectable)
    Console.WriteLine(item.Name);
//prints out:
//Item 1
//Item 5

請註意,即使 selected 包含不​​在 available 中的名稱,這也可以使用,例如,如果第二個列表在上一個問題中有 Item 4

轉載註明原文: LINQ中與多個實例/ IEqualityComparer問題的等價比較

猜你喜歡