How to sort a collection based on type in LINQ

試著忘記壹切 提交于 2020-08-27 05:22:06

问题


I have a collection like this,

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

Now I want to sort the collection based on type (A/B). How I can do this? Please help me.


回答1:


You can use the type information itself:

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

This will work for the two types you specified, but doesn't scale beyond them. It does allow you to specify the order explicitly (ie: if you want "B" elements before "A", you could make it work using this technique).

If you need to support many types, and the ordering doesn't need to be specified in advance, you could do something like:

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

This would handle any number of types (ie: a C and a D subtype, too), and order them by their full type name.




回答2:


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

Then take your pick from:

collection.OrderBy(OrderOnType)

or

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

Depending on whether you want in-place sorting or not. You could put OrderOnType into the lambda if you really wanted, but this seems more readable to me, and I prefer to keep lambdas for when they add rather than reduce readability.




回答3:


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

Will give you a sequence with all the As then all the Bs




回答4:


This is going to order so A will be the first and B the second.

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

This following console project confirms:

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

In this case I am assuming you only have A and B. If you have more types you would have to create a comparer to return a value for each type. You could also have a property on the base class which would set the order of the elements, then you could sort the list with this property.




回答5:


Does

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

do what you need?




回答6:


EDIT: I think this is what you want:

If you don't mind sorting 'out of place' and reassigning the list, this should work:

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

or depending on your needs, something like:

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

If it must be in-place, then writing a custom comparer and list.Sort is probably the best choice.


To group the items by type, you can use GroupBy:

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

This uses deferred execution.

Alternatively, you can put the 'groups' into a data-structure like this:

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

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

If you are only looking for a certain type:

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



回答7:


Something like this works for me.

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

My code:

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:


it's a later answer but something easy that might work for others and worked during my testing to implement something like this

var orderedList = animalList.OrderBy(x=> x.GetType().Name);

foreach(var animal in orderedList)
{
    System.Console.WriteLine(animal.Name);
}

output would look like:

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

where the animalList looks something like:

animalList = new List<IAnimal>();

and IAnimal interface/implementations:

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;}
}

further more, something like specifying an enum type that represents your class order might help. Example:

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

then it is as easy as

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


来源:https://stackoverflow.com/questions/3874853/how-to-sort-a-collection-based-on-type-in-linq

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!