2010-10-06 74 views
6

我有這樣一個集合的集合,如何排序基於類型的LINQ

Class Base{} 
Class A : Base {} 
Class B : Base {} 

List<Base> collection = new List<Base>(); 
collection.Add(new A()); 
collection.Add(new B()); 
collection.Add(new A()); 
collection.Add(new A()); 
collection.Add(new B()); 

現在我要進行排序基於類型(A/B)的集合。我如何做到這一點?請幫幫我。

回答

7

您可以使用類型信息本身:

collection.Sort((a,b) => 
    { 
     bool aType = a.GetType() == typeof(A); 
     bool bType = b.GetType() == typeof(A); 
     return aType.CompareTo(bType); 
    }); 

這會爲你指定的兩種類型的工作,但沒有規模超越他們。它確實允許你明確地指定順序(即:如果你想在「A」之前使用「B」元素,你可以使用這種技術使其工作)。

如果你需要支持多種類型,並且排序並不需要提前指定的,你可以這樣做:

collection.Sort((a,b) => a.GetType().FullName.CompareTo(b.GetType().FullName)); 

這將處理任何數量的類型(即:一個C和一個D子類型),並按全名命名。

+0

@Anthony:是的 - 這裏沒有檢查null。現在,除了A或B以外的任何東西都將被視爲B ... – 2010-10-06 16:49:22

+0

是的,我又看了一遍,發現它不是*完全*因爲沒有考慮類型' C:基地'。我很可能會做'a.GetType()。Name.CompareTo(b.GetType()。Name')(以及任何空檢查,如果它們是相關的)。但是誰知道,也許只有兩種類型,也許'A'確實是'Foo','B'確實是'Bar','Foo'應該在'Bar'前面。 – 2010-10-06 16:51:07

+0

@Anthony:在原始問題中沒有足夠的信息來完全確定這一點 - 這可以讓你更好地控制排序的發生,但很難知道OP真正想要的是什麼...... – 2010-10-06 16:58:12

0

編輯:我想這是你想要的東西:

如果你不介意整理「出位」,並重新分配名單,這應該工作:

collection = collection.GroupBy(item => item.GetType()) 
         .SelectMany(g => g) 
         .ToList(); 

或根據您的需求是這樣的:

collection = collection.OrderBy(item => item.GetType().FullName) 
         .ToList(); 

如果一定要就地,然後寫一個自定義比較和list.Sort可能是最好的選擇。


要按類型分組的項目,你可以使用GroupBy

var groupedItems = collection.GroupBy(item => item.GetType()); 

它使用延遲執行。

或者,你可以把「羣體」到數據結構是這樣的:

var itemsByTypeLookUp = collection.ToLookup(item => item.GetType()); 

foreach(A a in itemsByTypeLookUp[typeof(A)]) 
{ 
    ... 
} 

如果你只是在尋找某一類型:

var itemsOfTypeA = collection.OfType<A>(); 
+0

這組他們,但排序好好嘗試一下列表... – 2010-10-06 16:43:48

+0

@裏德•科普塞:謝謝,編輯。 – Ani 2010-10-06 16:48:26

1

是否

collection.Where(entry => entry is A).Concat(collection.Where(entry => entry is B)) 

你需要什麼?

1

這是要命令所以A將是第一個和B第二個。

var xx = list.OrderBy(x => x.GetType() == typeof(B)).ToList(); 

這以下控制檯項目確認:

class Program 
{ 
    public class X { } 
    public class A : X { } 
    public class B : X { } 
    static void Main() 
    { 
     List<X> list = new List<X>(); 
     list.Add(new B()); 
     list.Add(new A()); 
     list.Add(new B()); 
     list.Add(new A()); 
     list.Add(new A()); 

     // A.GetType() == typeof(B) will be "0" making the type A go first 
     // B.GetType() == typeof(B) will be "1" making the type B go last 
     var xx = list.OrderBy(x => x.GetType() == typeof(B)).ToList(); 

     Console.ReadLine(); 
    } 
} 

在這種情況下,我假設你只有AB。如果你有更多的類型,你將不得不創建一個比較器來爲每種類型返回一個值。你也可以在基類上設置一個屬性來設置元素的順序,然後你可以用這個屬性對列表進行排序。

+0

如果有類型'C:Base'會怎麼樣? – 2010-10-06 16:49:31

+0

@Anthony:他必須用「Comparer」處理每種類型。除非基類有一個名爲'Order'的屬性,你可以通過這個屬性來定購。既然他只說'A'和'B',我相信這對他來說是最短的解決方案。 – BrunoLM 2010-10-06 16:55:34

+0

@Jon Hanna提供了一個有趣的方式來處理訂單,另一個功能,你可以提供任何你想要的索引。當然,每個新類型都必須修改該函數。或者,就你的情況而言,你可以通過'x => x.GetType()。Name'命令,這會將A放在B之前的B之前。當然,這可能是A和B不是真的A和B,字母排序不起作用。所以,是的,除了更多的信息,很難說什麼答案是合適的。我只是想出了一個C類。 – 2010-10-06 17:01:56

6
private static int OrderOnType(Base item) 
{ 
    if(item is A) 
    return 0; 
    if(item is B) 
    return 1; 
    return 2; 
} 

然後從你挑:

collection.OrderBy(OrderOnType) 

collection.Sort((x, y) => OrderOnType(x).CompareTo(OrderOnType(y))); 

根據您是否想就地分揀與否。如果你真的想要的話,你可以把OrderOnType放入lambda表達式中,但這對我來說似乎更易讀,而且我更喜歡在添加lambda時保留lambda表達式,而不是減少可讀性。

4
collection.OrderBy(i => i.GetType() == typeof(A) ? 0 : 1); 

會給你一個序列的所有A當時的所有B小號

+0

如果有'C:Base'類型,該怎麼辦? – 2010-10-06 16:50:16

+0

@Anthony Pegram - 那麼這種方法將不起作用。我想你可以在Name類型上訂購,但是如果你想在這種情況下訂單成爲A,C,B呢?如果這種方法過於簡單,則需要添加更多細節。 – Lee 2010-10-06 17:06:58

+0

我同意。這是一個思想練習。根據Jai對Reed的回答的評論,只有2種類型,這些答案都是適當的。 – 2010-10-06 17:11:15

0

像這樣的事情對我的作品。

collection.OrderBy(p => p.GetType().Equals(typeof(B))).ThenBy(p => p.GetType().Equals(typeof(A))).ToList(); 

我的代碼:

class Employer; 
class Doctor : Employer 
class Administrator : Employer 
class Nurse : Employer 
List<Employer> collection = new List<Employer>(); 
collection.Add(new Doctor()); 
collection.Add(new Administrator()); 
collection.Add(new Doctor()); 
collection.Add(new Nurse()); 
collection.Add(new Administrator()); 
collection = collection.OrderBy(p => p.GetType().Equals(typeof(Nurse))).ThenBy(p => p.GetType().Equals(typeof(Doctor))).ThenBy(p => p.GetType().Equals(typeof(Administrator))).ToList();