A quick guide to LINQ with examples

A quick guide to LINQ with examples

Today a friend asked me about LINQ. I guess she was studying for a technical interview. So, dear Alice, this is what LINQ is, and these are the most common LINQ methods with examples. All you need to know in 15 minutes or less.

Language-Integrated Query (LINQ) is the declarative way of working with collections in C#. LINQ can be used with databases and XML files too. LINQ can be found as an API syntax, using extensions methods on the IEnumerable type, or as language-level query syntax, using a SQL-like syntax.

LINQ is declarative

LINQ is declarative. It means you write your code stating the results you want instead of doing every step to get those results.

With LINQ, you write code to “filter a collection based on a condition”. Instead of writing code to “grab an element, check if it satisfies a condition, then move to the next element, check again…“, etc.

LINQ is a better alternative to query collections using for, foreach, or any other loop. Because with LINQ, you can write more expressive and compact code.

Our first example: Movies

Let’s see an example. Let’s start with the collection of movies we have watched. We have a Movie class with a name, release year, and rating. Let’s find our favorite movies, the ones with a rating greater than 4.5.

Waiting at a cinema before a movie starts
Photo by Erik Witsoe on Unsplash

A console program to print our favorite movies looks like the next code listing. Notice the foreach loop and the comparison using an if statement to look for ratings greater than 4.5.

using System;
using System.Collections.Generic;

namespace QuickLinqGuide
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var movies = new List<Movie>
            {
                new Movie("Titanic", 1998, 4.5f),
                new Movie("The Fifth Element", 1997, 4.6f),
                new Movie("Terminator 2", 1991, 4.7f),
                new Movie("Avatar", 2009, 5),
                new Movie("Platoon", 1986, 4),
                new Movie("My Neighbor Totoro", 1988, 5)
            };

            var favorites = new List<Movie>();
            foreach (var movie in movies)
            {
                if (movie.Rating > 4.5)
                {
                    favorites.Add(movie);
                }
            }

            foreach (var favorite in favorites)
            {
                Console.WriteLine($"{favorite.Name}: [{favorite.Rating}]");
            }

            Console.ReadKey();
        }
    }

    internal class Movie
    {
        public Movie(string name, int releaseYear, float rating)
        {
            Name = name;
            ReleaseYear = releaseYear;
            Rating = rating;
        }

        public string Name { get; set; }
        public int ReleaseYear { get; set; }
        public float Rating { get; set; }
    }
}

The previous example prints the next four movies.

The Fifth Element: [4.6]
Terminator 2: [4.7]
Avatar: [5]
My Neighbor Totoro: [5]

Change the example to use your own movies and see which ones are your favorite movies!

Our first LINQ method: Where

LINQ methods are extension methods on the IEnumerable type. This type represents objects we can loop through. Like, arrays, lists, dictionaries, among others.

In case you missed it…You can add methods to a type without modifying it with extension methods. They are static methods defined outside the declaration of a type. But, they look like regular methods when you use them.

To work with LINQ, you need to be comfortable with delegates and lambda functions. A lambda function is a method with only the parameters and the body. To learn more about delegates and lambda functions, check my post on the difference between Func and Action.

How to filter a collection with Where?

Now, to the actual example. To start using LINQ methods, let’s add the statement using System.Linq.

Next, we want to filter our list of movies to keep only the ones with a rating greater than 4.5. The LINQ method to filter collections is Where.

The Where method returns a new collection with all the elements that meet a condition.

Let’s replace the first foreach statement from our example with the Where method. And use the condition inside the if statement as the filter condition for the Where method. Our example looks like this:

using System;
using System.Collections.Generic;
// This is the new using statement
using System.Linq;

namespace QuickLinqGuide
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var movies = new List<Movie>
            {
                new Movie("Titanic", 1998, 4.5f),
                new Movie("The Fifth Element", 1995, 4.6f),
                new Movie("Terminator 2", 1999, 4.7f),
                new Movie("Avatar", 2010, 5),
                new Movie("Platoon", 1986, 4),
                new Movie("My Neighbor Totoro", 1988, 5)
            };

            var favorites = movies.Where(movie => movie.Rating > 4.5);

            foreach (var favorite in favorites)
            {
                Console.WriteLine($"{favorite.Name}: [{favorite.Rating}]");
            }

            Console.ReadKey();
        }
    }

    // The Movie class remains the same
}

We replaced the foreach and if statements with a single line of code:

var favorites = movies.Where(movie => movie.Rating > 4.5);

More compact, isn’t it? Also, we turned the condition inside the if statement into a lambda function.

Separate methods and lambda functions

Instead of lambda functions, we can use private methods with LINQ too. For our example, let’s create an IsFavorite() method that receives Movie as a parameter and returns bool. For example,

private bool IsFavorite(Movie movie)
{
    return movie.Rating > 4.5;
}

Then, we can use IsFavorite inside the Where method to filter our movies. Like this,

var favorites = movies.Where(movie => IsFavorite(movie));

We can simplify things ever further. Since the IsFavorite method only has one parameter, we can remove the intermediate variable, like this,

var favorites = movies.Where(IsFavorite);

LINQ and immutability

LINQ methods don’t change the original collection. They return a result without modifying the original collection.

From our example, when we used the Where method, it returned a new collection. It didn’t remove any elements from the original movies list.

Most common LINQ methods

So far, we have seen only one LINQ method, Where. Let’s see other frequently-used methods.

Select

Select transforms every element of a collection. It applies a function to every element.

Let’s find only the names of our favorite movies.

var favorites = movies.Where(movie => movie.Rating > 4.5)
                      .Select(movie => movie.Name);

foreach (var name in favorites)
{
    Console.WriteLine(name);
}

// The Fifth Element
// Terminator 2
// Avatar
// My Neighbor Totoro

Notice how this time we have nested two LINQ methods. The result from Where will be the input of Select.

For more readability, we often align the nested LINQ methods vertically by the (.) dot.

Any

The Any method check if a collection is empty. Also, it checks if a collection has at least one element matching a condition. It returns either true or false. It doesn’t return a new collection.

Let’s see if we have watched movies with a low rating.

var hasAnyMovies = movies.Any();
// true

var hasBadMovies = movies.Any(movie => movie.Rating < 2);
// false

All

Unlike Any, All check if every element inside a collection matches a condition. It also returns either true or false instead of a new collection.

Let’s see if we have only watched really-good movies.

var weHaveSeenReallyGoodMovies = movies.All(movie => movie.Rating >= 4.5);
// false

GroupBy

GroupBy groups the elements of a collection based on a key. It returns a collection of “groups” or “buckets” organized by a key.

Let’s group our movies by rating.

var groupedByRating = movies.GroupBy(movie => movie.Rating);

foreach (var group in groupedByRating)
{
    Console.WriteLine($"Rating: {group.Key}");

    foreach (var movie in group)
    {
        Console.WriteLine($"{movie.Name}");
    }
    Console.WriteLine();
}

This will be the output.

Rating: 4.5
Titanic

Rating: 4.6
The Fifth Element

Rating: 4.7
Terminator 2

Rating: 5
Avatar
My Neighbor Totoro

Rating: 4
Platoon

Also, GroupBy could transforms each group. This time, let’s count the movies with the same rating.

var groupedByRating = movies.GroupBy(movie => movie.Rating,
                                    (rating, movies) => new { Rating = rating, Count = movies.Count() });

foreach (var group in groupedByRating)
{
    Console.WriteLine($"{group.Rating}: [{group.Count}]");
}

Notice the second parameter of the GroupBy. It’s a Func with the key and the elements of each group. We also used an anonymous object new { Rating=..., Count=... }. It’s like a regular object, but we didn’t specify a name.

And this is the output of counting movies by rating.

4.5: [1]
4.6: [1]
4.7: [1]
5: [2]
4: [1]

First and FirstOrDefault

First and FirstOrDefault return the first element in a collection. First throws an exception if the collection is empty. Unlike First, FirstOrDefault returns a default value if the collection is empty.

Also, First and FirstOrDefault return the first element matching a condition.

Let’s find the oldest film we have watched.

var oldest = movies.OrderBy(movie => movie.ReleaseYear)
                   .First();

// Platoon

This time, we used the OrderBy to sort the movies collection by release year. Two examples for the price of one!

In the same spirit of First and FirstOrDefault, we have Last and LastOrDefault. But, they return the last element instead of the first one.

Cheatsheet

There are more LINQ methods than the ones we’ve seen so far. These are some of them.

Method Function
Where Filter a collection
Select Transform every element of a collection
Any Check if a collection is empty
All Check if every element satisfies a condition
Count Count all elements of a collection
Distinct Find the unique elements of a collection
GroupBy Group the elements of a collection based on a key
OrderBy Sort a collection based on a key
First Find the first element of a collection. Throw if the collection is empty
FirstOrDefault Same as First but it returns a default value if it’s empty
Last Find the last element of a collection. Throw if the collection is empty
LastOrDefault It returns a default value if it’s empty, instead
Single Find only one element in a collection matching a condition. Throw, otherwise
SingleOrDefault It returns a default value if there isn’t one matching element, instead
Take Pick the first n consecutive elements of a collection
TakeWhile Pick the first consecutive elements that satisfy a condition
Skip Return a collection without the first n consecutive elements
SkipWhile Return a collection without the first consecutive elements that satisfy a condition
Sum Sum the elements of a collection
Min, Max Find the smallest and largest element of a collection
ToDictionary Convert a collection into a dictionary

LINQ Query syntax: A matter of taste

Up to this point, we have seen LINQ as extension methods. But, we can find LINQ as language-level query syntax too.

This is the same example to find our favorite movies using language-level query syntax.

var bestOfAll = from movie in movies
                where movie.Rating > 4.5
                select movie;

It looks like SQL, isn’t it? And, this is the same code using extension methods. We’ve seen this before.

var bestOfAll = movies.Where(movie => movie.Rating > 4.5);

Which LINQ syntax should you use? Prefer the syntax used in your current codebase. If your code uses extensions methods on IEnumerable, continue to do that.

Popcorn
Speaking of taste. Photo by Christian Wiediger on Unsplash

But, there is one advantage of using query syntax over extension methods. You can create intermediate variables with the let keyword.

Find largest files on the Desktop folder

Let’s find all files inside our Desktop folder larger than 10MB. And, let’s use let to create a variable.

using System;
using System.IO;
using System.Linq;

namespace QuickLinqGuide
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            var desktop = new DirectoryInfo(desktopPath);

            var largeFiles = from file in desktop.GetFiles()
                             // We can create intermediate variables with 'let'            
                             let sizeInMb = file.Length / 1024 / 1024
                             where sizeInMb > 10
                             select file.Name;

            foreach (var file in largeFiles)
            {
                Console.WriteLine(file);
            }

            Console.ReadKey();
        }
    }
}

From the above example, the file size is in bytes. Then, notice how we declared an intermediate variable. Like this,

let sizeInMb = file.Length / 1024 / 1024

Three common LINQ mistakes

Now that we know what LINQ is and the most common LINQ methods, let’s go through three common mistakes we make when using LINQ methods in our code.

1. Write Count instead of Any

Always prefer Any over Count to check if a collection has elements or an element that meets a condition.

Do movies.Any() instead of movies.Count() > 0.

2. Write Where followed by Any

You can use a condition with Any instead of filtering first with Where to later use Any.

Do,

movies.Any(movie => movie.Rating == 5)

Instead of,

movies.Where(movie => movie.Rating == 5).Any()

The same applies to the Where method followed by FirstOrDefault, Count, or any other method that receives a filter condition.

3. Use FirstOrDefault without null checking

Make sure to always check if you have a result when working with FirstOrDefault, LastOrDefault, and SingleOrDefault. If there isn’t one, you will get the default value of the collection type.

private static void Main(string[] args)
{
    var movies = new List<Movie>
    {
        new Movie("Titanic", 1998, 4.5f),
        new Movie("The Fifth Element", 1995, 4.6f),
        new Movie("Terminator 2", 1999, 4.7f),
        new Movie("Avatar", 2010, 5),
        new Movie("Platoon", 1986, 4),
        new Movie("My Neighbor Totoro", 1988, 5)
    };

    var worst = movies.FirstOrDefault(movie => movie.Rating < 2);

    Console.WriteLine($"{worst.Name}: [{worst.Rating}]");
    //                  ^^^^^^^^^^^^ 
    // System.NullReferenceException: 'Object reference not set to an instance of an object.'
    //
    // worst was null.

    Console.ReadKey();
}

For objects, the default value would be a null reference. And you know what happens when you try to access a property or method on a null reference?… Yes, It throws NullReferenceException.

To make sure we always have a non-nullable result when working with FirstOrDefault, we can use the the DefaultIfEmpty method. It returns a new collection with a default value if the input collection is empty.

var worst = movies.Where(movie => movie.Rating < 2)
                  .DefaultIfEmpty(new Movie("Catwoman", 2004, 3))
                  .First();

Conclusion

Voilà! That’s it, Alice. That’s all you need to know to start working with LINQ in your code in 15 minutes or less. There’s also this project MoreLINQ with more extension methods, like CountBy, DistinctBy, MinBy and MaxBy. With LINQ, you can write more compact and expressive code. The next time you need to write logic using loops, give LINQ a try!

Learn how to use LINQ to refactor conditionals on C# idioms. For more C# content, check C# Definitively Guide for a list of subjects every intermediate C# developer should know. And, my top 10 or so best C# features for other cool C# features.

Happy LINQ time!