In this article, we are going to learn about Sorting and Filtering with LINQ. The Language-Integrated Query (LINQ) provides a robust set of language extensions that we can use to shape query results according to our current needs. LINQ not only allows for a type-safe way to retrieve data but also a means to sort or filter that queried data in the same way.

If unfamiliar with LINQ, or need a refresher on its many advantages or disadvantages, please read our LINQ Basic Concepts article.

To download the source code for this article, you can visit our GitHub repository.

Let’s see how we can use LINQ to get exactly the data we want!

How Do We Sort in LINQ?

First off, what do we mean by sorting? Sorting allows us to be able to order the elements in a set of data by one or more specific attributes. Additionally, we can of course nest the sorting to order each element in the set by whatever priority we desire.

For our sorting examples we will be using the list of custom shape objects:

var shapeList = new List<Shape>()
{
    new Shape() { ShapeId = 0, ShapeType = "Square", ShapeHeight = 2, ShapeWidth = 2,Is3D = false },
    new Shape() { ShapeId = 1, ShapeType = "Cone", ShapeHeight = 3, ShapeWidth = 1,Is3D = true },
    new Shape() { ShapeId = 2, ShapeType = "Rectangle", ShapeHeight = 4, ShapeWidth = 6,Is3D = false }
};

LINQ Sorting Operators

To sort this data we will use one of LINQ’s five sorting operators.

OrderBy

The OrderBy operator sorts the data in ascending (beginning with the smallest and ending with the largest) order.

We begin by passing a lambda expression to the Orderby method:

var sortedList = shapeList.OrderBy(sl => sl.ShapeType).ToList();

The lambda expression represents each iteration through the collection and what property of the item we wish to sort by.

Notice that the method itself does not modify the original collection. Also, because this method is an extension of the IEnumerable type, we need to cast the IEnumerable result to a List to get a new List with the data ordered as we want.

OrderByDescending

To accomplish the same but in descending order, we can use the OrderByDescending method: 

var sortedList = shapeList.OrderByDescending(sl => sl.ShapeId).ToList();

Notice that for sake of example, we used a different property to order by this time.

We can inspect our results after we order them by ShapeType ascending and ShapeId descending respectively:

----------------------------------------
ShapeId: 1 ShapeType: Cone ShapeWidth: 1 ShapeHeight: 3 is3D: True
ShapeId: 2 ShapeType: Rectangle ShapeWidth: 6 ShapeHeight: 4 is3D: False
ShapeId: 0 ShapeType: Square ShapeWidth: 2 ShapeHeight: 2 is3D: False
----------------------------------------
----------------------------------------
ShapeId: 2 ShapeType: Rectangle ShapeWidth: 6 ShapeHeight: 4 is3D: False
ShapeId: 1 ShapeType: Cone ShapeWidth: 1 ShapeHeight: 3 is3D: True
ShapeId: 0 ShapeType: Square ShapeWidth: 2 ShapeHeight: 2 is3D: False
----------------------------------------

ThenBy

We can use the ThenBy method after the OrderBy method for nested sorting.  After the OrderBy method has ordered the data, we specify another property for the ThenBy method to use.

Like in the previous examples, we have to pass a lambda expression to ThenBy: 

var sortedList = shapeList.OrderBy(sl => sl.Is3D).ThenBy(sl => sl.ShapeId).ToList();

In this instance, we first want to order the list by whether or not the shapes are 3D, and then we want to sort by the shapes’ id number.

ThenByDescending

We will do the same with the ThenByDescending method: 

var sortedList = shapeList.OrderByDescending(sl => sl.Is3D).ThenByDescending(sl => sl.ShapeId).ToList();

By using the ThenByDescending method, we sort the second set of results in descending order.

Reverse

Reverse is the  sorting method that does not modify the original collection of items:

private static IEnumerable<Shape> LINQSortReverse(IEnumerable<Shape> shapeList)
{
    var reversedList = shapeList.Reverse();

    return reversedList;
}

After we call the LINQ’s extension Reverse method, from the Enumerable class, it will return a reversed list while leaving the main list untouched.

How Do We Filter in LINQ?

Filtering extracts elements from the dataset based on whatever criteria we specify.

LINQ Filtering Operators

LINQ accomplishes filtering with two filtering operators.

Where

The Where method is a powerful extension that allows us to filter the collection based on the criteria we specify:  

var filteredList = shapeList.Where(sl => sl.ShapeHeight < 4).ToList();

In this specific example, we extract all the shapes from the list that are less than four.

If we need to filter by more than one criteria, we can utilize the logical && or || operators. However, if this becomes too verbose, we can move the expression we pass to Where to a separate method for better readability: 

var filteredList = shapeList.Where(sl => CheckShape(sl)).ToList();

That said, let’s check the CheckShape method implementation:

private static bool CheckShape(Shape s)
{
    if(s.ShapeWidth < 3 && s.Is3D)
    {
        return true;
    }
    return false;
}

This method must return a bool to properly be handled by the Where clause.

OfType

OfType is a unique extension method that checks to see if the current element can be cast to a specified type, if not, then the element is removed.

Accordingly, this can be useful to make certain that all the elements in this collection are our custom Shape object:  

var filteredList = shapeList.OfType<Shape>().ToList();

The resulting list will now only contain Shape objects.  This is extremely useful in situations where we want to use a generic collection of objects or inheritance.

Using Extension Methods

Custom static methods – extension methods – allow us to customize or add functionality to how we sort or filter the results of a LINQ query.

To learn more about extension methods, please read our C# Intermediate – Static Members, Constants, and Extension Methods article.

Let’s create an extension method to filter by removing null items from a List collection:

public static List<T> RemoveNullFilter<T>(this List<T> list)
{
    foreach (T item in list.ToList())
    {
        if(item == null)
        {
            list.Remove(item);
        }
    }
    return list;
}

The method iterates through every item in the copy of the list, removing those that are null in the original one and returning the modified list. 

To learn more about removing elements from a list in an iteration, and why we use ToList inside the foreach loop, feel free to read our How to Remove Elements From a Generic List in Iteration With C# article.

We call this in the same way that we called the Reverse method: 

shapeList.RemoveNullFilter();

Conclusion

In this article, we covered a few of the excellent ways to implement Sorting and Filtering with LINQ. We hope that using this information you will be able to be sorting and filtering your data with LINQ to get exactly what you need out of each query!