C#: Using LINQ to find, filter and group objects in a collection

Posted on 06th May 2021

Language INtegrated Query or LINQ is a .NET technology gives the capability to query data from various data sources such as .NET collections, XML documents, SQL databases, ADO.NET datasets. LINQ is integrated with C# and VB.NET and the query construct and syntax is the same no matter which data source you are querying.

You can use LINQ with any collection that exposes IEnumerable or IEnumerable<T> interface. LINQ queries can be written in two ways - using query syntax or method syntax. The following example shows a simple LINQ query on a custom collection of class objects.

class Car
{
  public string Make { get; set; }
  public string Model { get; set; }
  public int Year { get; set; }
  public string Transmission { get; set; }
  public string FuelType { get; set; }
}

List Cars = new List()
{
 new Car {Make = "BMW", Model = "3 Series", Year = 2018, Transmission = "Manual", FuelType = "Petrol"},
 new Car {Make = "Mercedes-Benz", Model = "E Class", Year = 2017, Transmission = "Automatic", FuelType = "Petrol"},
 new Car {Make = "Audi", Model = "Q7", Year = 2020, Transmission = "Automatic", FuelType = "Diesel"},
 new Car {Make = "Toyota", Model = "Auris", Year = 2015, Transmission = "Automatic", FuelType = "Hybrid"},
 new Car {Make = "Ford", Model = "Focus", Year = 2013, Transmission = "Manual", FuelType = "Petrol"},
};

// LINQ Query to get all cars in Query Syntax
var queryAllCars = from car in Cars select car;

// queryAllCars is IEnumerable 
foreach (var car in queryAllCars)
{
  Console.WriteLine($"{car.Make} - {car.Model} ");
}

Output:

BMW - 3 Series
Mercedes-Benz - E Class
Audi - Q7
Toyota - Auris
Ford - Focus

In the above example the LINQ query was written in query syntax. The same can be achieved using method syntax as below.

var queryAllCars = Cars.Select(car => car);

In method systax, you call the select method and pass a lambda expression(car => car) as parameter

Note that performance-wise there is no difference between query syntax and method syntax. Query syntax is more readable while some operations can be done only using method syntax.

Rest of this article shows you examples of how to use LINQ methods to perform various operations on a sequence.

Using Where method to filter

The Where method can be used to filter a sequence based on the predicate. A predicate is expressed using lambda expression and predicates return boolean values - true or false.

To filter all automatic cars from the Cars sequence.

 IEnumerable AutomaticCars = Cars.Where(car => car.Transmission == "Automatic");

The predicate in the above code is

car => car.Transmission == "Automatic"

This LINQ query returns the following cars

Mercedes-Benz-E Class
Audi-Q7
Toyota-Auris

The Where method also allows you to use each elements index in the predicate. For example, the below code get all cars with automatic transmission and index position not equal to 1.

IEnumerable AutomaticCars = Cars.Where((car,index) => car.Transmission == "Automatic" && index != 1);

This will filter out Mercedes-Benz from the result as it is index position 1 on the collection.

Audi-Q7
Toyota-Auris

The following table lists commonly used LINQ methods with example usage on the Cars sequence.

MethodDescription & Usage
Selecting
ElementAt

Returns the element at the specified index position in the sequence. Index is zero-based so index of the first element is 0.

Car c = Cars.ElementAt(2);
/* Returns Audi */
First

Returns the first element of a sequence.

Car firstCar = Cars.First();
/* Returns BMW  */

Returns the first element that match the specified condion in the sequence.

Car firstCar = Cars.First(c => c.Transmission == "Automatic");
/* Returns Mercedes-Benz  */
Last

Returns the last element of a sequence.

Car lastCar = Cars.Last();
/* Returns Ford  */

Returns the last element that match the specified condion in the sequence.

Car lastCar = Cars.Last(c => c.Transmission == "Automatic");
/* Returns Toyota  */
Select

Transforms each object in the sequence to a different form. In the following example the Make and Model of each car is combined to return a new list of strings.

IEnumerable<string> CarMakeModel  = Cars.Select(car => car.Make + " " + car.Model);
/* Returns a list containing the following strings.
  BMW 3 Series
  Mercedes - Benz E Class
  Audi Q7
  Toyota Auris
  Ford Focus
 */
Single

This method call returns a single element in the sequence that satisfies the specified condition. An error is thrown if there are more than one element that maches the condition.

Car HybridCar = Cars.Single( c => c.FuelType == "Hybrid");
Take

Returns the first N number of elements from the sequence where N is a positive integer.

IEnumerable<Car> FirstTwo = Cars.Take(2);
TakeLast

Returns the last N number of elements from the sequence where N is a positive integer.

IEnumerable<Car> LastTwo = Cars.TakeLast(2);
TakeWhile

Returns elements from the starting of the sequence while the condition is true.

IEnumerable<Car> query = Cars.TakeWhile(c => c.FuelType != "Diesel");
/* Returns BMW and Mercedes-Benz */

You could also use the index of the element in the condition. The following example return first 3 elements.

IEnumerable<Car> query = Cars.TakeWhile((c,index) => index != 3);
Checking
Any

Check if any element exist in the sequence.

bool check = Cars.Any();
/* Returns: True */

Check if an element that matches the specified condition exist in the sequence.

bool check = Cars.Any(c => c.FuelType == "Hybrid");
/* Returns: True */
All

Check if the specified condition holds true for all elements in the sequence.

bool AllPetrolCars = Cars.All(c => c.FuelType == "Petrol");
/* Returns: False */
Methods for Aggregate functions
Count

If no argument is specificed, Count returns the number of elements in the sequence. If a predicate is specified, Count returns the number of elements that match the specified condition.

int count = Cars.Count();
/* count = 5 */
int countManual = Cars.Count(c => c.Transmission == "Manual");
/* count = 2 */
MaxReturns the maximum value in a sequence.
 int maxYear = Cars.Max(c => c.Year);
 /* returns 2020 */
MinReturns the minimum value in a sequence.
 int maxYear = Cars.Min(c => c.Year);
 /* returns 2013 */
Conversion
ToDictionary

Converts the IEnumberable collection to a dictionary (key-value pairs).

 Dictionary dict =  Cars.ToDictionary(c => c.Make, c => c.Model);
 foreach (var kvp in dict)
 {
	Console.WriteLine("Key {0}: {1}", kvp.Key, kvp.Value);
 }

You will get a duplicate key error if the key is not unique in the collection.

ToList

Converts the IEnumberable collection to a List.

 List list =  Cars.Select(c => c.Make).ToList();
 /* Returns a list containing the following strings.
  BMW
  Mercedes
  Audi
  Toyota
  Ford
 */

Post a comment

Comments

Nothing yet..be the first to share wisdom.