【问题标题】:How to sort a collection based on type in LINQ如何根据 LINQ 中的类型对集合进行排序
【发布时间】:2011-04-21 22:49:18
【问题描述】:

我有这样的收藏,

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) 对集合进行排序。我怎么能做到这一点?请帮帮我。

【问题讨论】:

    标签: c# linq .net-3.5


    【解决方案1】:

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

    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 子类型),并按它们的完整类型名称对它们进行排序。

    【讨论】:

    • @Anthony:是的 - 这里没有检查 null。现在,除了 A 或 B 之外的任何东西都将被视为 B...
    • 是的,我又看了一遍,结果发现它不是完全因为没有考虑到类型C : Base。我可能会做a.GetType().Name.CompareTo(b.GetType().Name) (以及任何空检查,如果它们是相关的)。但是谁知道呢,也许只有两种类型,也许A真的是FooB真的是BarFoo应该在Bar之前。
    • @Anthony:原始问题中确实没有足够的信息来完全确定这一点 - 这使您可以更好地控制排序的发生方式,但很难知道 OP 真正想要什么......
    • @Reed,我完全同意。这都是假设。
    • @Anthony:我重新设计了它——现在听起来更好了吗? (顺便说一句,我使用了全名,以防命名空间冲突……)
    【解决方案2】:
    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 以供它们添加而不是降低可读性。

    【讨论】:

      【解决方案3】:
      collection.OrderBy(i => i.GetType() == typeof(A) ? 0 : 1);
      

      会给你一个包含所有As 然后所有Bs 的序列

      【讨论】:

      • 如果有C : Base类型怎么办?
      • @Anthony Pegram - 那么这种方法就行不通了。我想您可以按 Name 类型订购,但是如果您希望在这种情况下订购 A、C、B 怎么办?如果这种方法过于简单,问题需要补充更多细节。
      • 我同意。这是一个思想练习。根据 Jai 对 Reed 答案的评论,只有 2 种类型,这使得其中许多答案是合适的。
      【解决方案4】:

      这是要订购的,所以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。如果您有更多类型,则必须创建一个比较器来为每种类型返回一个值。你也可以在基类上有一个属性来设置元素的顺序,然后你可以使用这个属性对列表进行排序。

      【讨论】:

      • 如果有C : Base类型怎么办?
      • @Anthony:他必须用Comparer 处理每种类型。除非基类有一个名为 Order 的属性,并且您可以按此属性订购。因为他只说AB 我相信这对他来说是最短的解决方案。
      • @Jon Hanna 提供了一种有趣的方式来处理订单,还有另一个功能,您可以提供您想要的任何索引。当然,必须使用每种新类型修改该功能。或者,在您的情况下,您可以按x =&gt; x.GetType().Name 排序,这会将A 放在B 之前,然后放在C 之前。当然,A 和B 可能不是真正的A 和B,并且字母排序不起作用。所以,是的,除非提供更多信息,否则很难说什么答案是合适的。我只是把C类作为一个想法扔掉了。
      • @Anthony,如果你想要一个代码定义的排序顺序,但也有 an 顺序(保证相同类型的对象在一起)与其他类型,你可以做OrderBy 按照 Bruno 的方法或我的方法,然后是类型全名的 ThenBy。
      【解决方案5】:

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

      做你需要的?

      【讨论】:

        【解决方案6】:

        编辑:我认为这就是你想要的:

        如果您不介意排序“不合适”并重新分配列表,这应该可以:

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

        【讨论】:

          【解决方案7】:

          这样的东西对我有用。

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

          【讨论】:

            【解决方案8】:

            这是一个稍后的答案,但可能对其他人有用并且在我的测试期间可以实现类似这样的东西的简单方法

            var orderedList = animalList.OrderBy(x=> x.GetType().Name);
            
            foreach(var animal in orderedList)
            {
                System.Console.WriteLine(animal.Name);
            }
            

            输出如下:

            Ape
            Ape
            Ape
            Ape
            Cat
            Cat
            Cat
            Cat
            Cat
            Dog
            Dog
            Dog
            Dog
            Dog
            

            animalList 看起来像这样:

            animalList = new List<IAnimal>();
            

            和 IAnimal 接口/实现:

            public class Ape : IAnimal 
            {
                public string Name => "Ape";
                public AnimalType AnimalType => AnimalType.Ape;
            }
            
            
            public class Dog : IAnimal 
            {
                public string Name => "Dog";
                public AnimalType AnimalType => AnimalType.Dog;
            }
            
            public class Cat : IAnimal
            {
                public string Name => "Cat";
                public AnimalType AnimalType => AnimalType.Cat;
            }
            
            public interface IAnimal 
            {
                 string Name {get;}
                 AnimalType AnimalType {get;}
            }
            

            此外,指定一个代表您的班级顺序的枚举类型可能会有所帮助。示例:

            public enum MyOrderType 
            {
                Ape = 1,
                Cat = 2,
                Dog = 3
            }
            

            那么就这么简单

            var orderedList = animalList.OrderBy(x=> x.AnimalType).ToList();
            

            【讨论】:

            • 我更新了我的回复以获取另一个示例并且更加清晰
            猜你喜欢
            • 1970-01-01
            • 2011-03-29
            • 2011-02-10
            • 1970-01-01
            • 2014-08-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多