2010-10-06 77 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();