LINQ to Objects Refresher

Hands up; how many of you have only really ever used LINQ for database access? How many of you only ever really believed it was for database access? Well it’s not, it’s potentially very useful in all manner of scenarios.

In this post, I’m going to go over a quick refresher about just what it is, and what you can use it for. Please note however, I’ll be using the functional approach, rather than the LINQ compound statement approach; that is I’ll be using:

var newList = sourceList.Select().Where(x => x.ID = 1);

form of writing code, rather than the following form:

var newlist = select from p in sourceList where ID = 1;

While the latter of the two may be easier to read, I find the former easier to work with, because it makes a logical chain of operational sequence when you read it backwards.

Let’s start however with:

Just What are LINQ to Objects

As the name implies, it’s regular LINQ, but used on Objects in memory, rather than on objects being called from a database as Linq to Sql does.

LINQ is largely misunderstood beyond this though, and just as it can do some very clever filtering and searching with objects from a database, if can perform just about all the same operations on collections held in your applications memory space.

Many of you will have used things like:

List<int> myListOfIntegers;

or

IEnumerable<string> myEnumerableListOfStrings;

In your .NET applications.  Anywhere you use lists and other collections like this, you can iterate over them, sort them and re-project them on the fly using LINQ. I’ll not be delving right down into the absolute depths in this post as we just don’t have enough space, but I will show you some of the basics that you’ll end up using quite a lot.

Let’s start with some simple filtering and searching. Let’s imagine you had the following data specified in your application:

List<Employee> myEmployees = new List<Employee>

{

  new Employee{ FirstName = "Bill", SecondName = "Cosby", Id = 1, Status = "Permanent" },

  new Employee{ FirstName = "Freddy", SecondName = "Kruger", Id = 2, Status = "Temporary" },

  new Employee{ FirstName = "Cindy", SecondName = "Sunshine", Id = 3, Status = "Permanent" },

  new Employee{ FirstName = "The", SecondName = "Stig", Id = 4, Status = "Temporary" }

}

Using the pre LINQ methodology, if you wanted all the entries that had a status of “Permanent”, you might do something like:

List<Employee> results = new
List<Employee>();

foreach(var employee in myEmployees)

{

  if(employee.Status == "Permanent")

  {

    results.Add(employee);

  }

}

While this is still nice and compact, and easy to read, using pure LINQ to Objects you can achieve the same in one line, like so:

List<Employee> results = myEmployees.Where(x => x.Status == "Permanent").ToList();

The new list will include only the entries from the first that match the criteria in the Where operator.

The strange operator you see in the where clause is called a ‘Lambda’, and yes I can hear the next question straight away.

What’s a Lambda?

Ok, let me get this one out of the way, immediately.

A lambda in C# is not in any way related to Lambda Calculus, or to Haskel portable lambdas or anything like that, it is simply a term that the C# engineering team decided to use for what is essentially a self closing, self executing block of code.

Lambdas are not some magical concept that the .NET development team dreamt up to make LINQ stand out from the crowd, and to be honest, you could just call them Anonymous functions or closures and get by with them perfectly well. At the end of the day a lambda is simply a block of code that can be passed as a parameter in a function definition.

Taking our where example above, we could change it to read:

List<Employee> results = myEmployees.Where(employee => employee.Status == "Permanent").ToList();

If you now compare that to the example using the for each loop, you can see that all we are really doing is passing in the inner code that was present in the if clause embedded inside the loop.

The ’employee =>’ part simply tells the ‘Where’ function that for every element in the list I passed to you, you need to assign to ’employee’ a copy of that current element, as we loop through them all.

Following from that, we then just make a statement that asks where to check a given property on the copy, and where then decides if it should or should not return it.

Filtering on a specific property is not all we can do; how about ordering things in a given sequence:

List<Employee> results = myEmployees.OrderByDescending(x => x.Id).ToList();

Will return our entire employees list, in reverse order with the first record starting at id = 4 and decreasing to id = 1.

What about if you only wanted the first permanent employee, with the lowest id (i.e.: they’ve been in the system the longest)? That’s simple also; you just need to combine different operators:

Employee result = myEmployees.OrderBy(x => x.Id).Where(x => x.Status == "Permanent").First();

The above code will give you exactly one employee.

If you use ‘FirstOrDefault‘ rather than ‘First‘ and NO employees exist in the list who have a Status of Permanent, then you’ll get a ‘null’ result rather than an Exception of ‘sequence matches no elements’ , which means you can quietly handle the fact that no entries exist and take some other course of action.

You can also perform Aggregates on your collection; let’s find out what all our ID’s add up to:

int result = myEmployees.Sum(x => x.Id);

Not very useful I know, but imagine if that was a salary level or age range.  How about an Average?

double result = myEmployees.Average(x => x.Id);

Once again, imagine if the value we were looking at contained a more useful value.

You can even use LINQ to Objects to ‘Page’ your data:

List<employee> pagedData = myEmployees.Skip(1).Take(2).ToList();

The above code will return a list that starts at the second record in the collection, and takes only 2 records, which in our example would be “Freddy” & “Cindy”.

You can also use ‘SkipWhile’ and ‘TakeWhile’ which will continue executing as long as a condition is true.  If for example you had a list of to-do items, you could say:

var firstBatchOfCompleteTasks = myTasks.SkipWhile(x => x.Status == "Done").TakeWhile(x => x.Status == "Not Done");

This would keep skipping until it found the first object that did not match the status in the Lambda, and then continue taking until the status in Lambda 2 returned a false condition.

There are dozens of operators and with a simple little command line program such as this:

using System;
using System.Collections.Generic;
using System.Linq;
 
namespace dnnutsandbolts
{
  static class Program
  {
    static void Main()
    {
      List<Employee> myEmployees = new List<Employee>
      {
        new Employee {FirstName = "Bill", SecondName = "Cosby", Id = 1, Status = "Permanent"},
        new Employee {FirstName = "Freddy", SecondName = "Kruger", Id = 2, Status = "Temporary"},
        new Employee {FirstName = "Cindy", SecondName = "Sunshine", Id = 3, Status = "Permanent"},
        new Employee {FirstName = "The", SecondName = "Stig", Id = 4, Status = "Temporary"}
      };
      var result = myEmployees.<Experiment with the Linq Calls here>
    }
  }
 
  public class Employee
  {
    public string FirstName { get; set; }
    public string SecondName { get; set; }
    public int Id { get; set; }
    public string Status { get; set; }
  }
}

The intellisense built in to Visual Studio will list them all for you and show you the type of Lambda they take.

Just about anything you can think about doing in an SQL Database, can be performed equally well on an in-memory collection, and if you start to use PLINQ rather than just normal LINQ, you get multi CPU aware operations virtually for free and without having to write your own multi threaded code to handle it.

Feel free to let me know what you’d like to see in this column, or if you have any burning questions you’d like answered seek me out on Twitter as @shawty_ds or in the Linked.NET (Lidnug) users group on Linked-In that I help run. Code happy.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read