How to sort a collection based on type in LINQ
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.
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.
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.
collection.OrderBy(i => i.GetType() == typeof(A) ? 0 : 1);
Will give you a sequence with all the A
s then all the B
s
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.
Does
collection.Where(entry => entry is A).Concat(collection.Where(entry => entry is B))
do what you need?
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>();
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();
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();
精彩评论