Common LINQ Methods
The LINQ methods you'll use every day. Where, OrderBy, ThenBy and Select filter, sort and reshape; Count, Sum, Average, Min, Max, Any, All and GroupBy summarize; First, FirstOrDefault, Take, Skip and Distinct pick items.
Journeyman · C# · Updated
After this lesson you can
- Filter, sort and reshape a collection with Where, OrderBy, ThenBy and Select
- Summarize a collection with Count, Sum, Average, Min, Max, Any, All and GroupBy
- Pick items from a collection with First, FirstOrDefault, Take, Skip and Distinct
Key takeaways
Wherekeeps the items you want,OrderByandThenBysort them, andSelectturns each item into something new, such as its name.Count,Sum,Average,Min,Max,AnyandAllend a chain with one value:Anyasks about at least one item,Allabout every item.GroupBysorts items into groups; each group has aKeyand is itself a collection you can count and total.- Use
Firstwhen there must be a match, since it throws if there's none; useFirstOrDefaultwhen none is a normal answer, then check fornull. Take(n)gives the firstnitems,Skip(n)the rest andDistinct()drops duplicates.Skip(10).Take(10)is page 2 of a list shown 10 at a time.
LINQ has dozens of methods, but a handful do most of the work. Every example in this lesson uses the same small café menu:
public class Product
{
public string Name { get; }
public string Category { get; }
public decimal Price { get; }
public Product(string name, string category, decimal price)
{
Name = name;
Category = category;
Price = price;
}
}
Coffee Drinks 3.50
Tea Drinks 2.75
Juice Drinks 4.00
Croissant Bakery 3.75
Muffin Bakery 2.50
Bagel Bakery 2.25
Filter, sort and reshape
| Method | Does | Like SQL's |
|---|---|---|
Where(p => ...) |
Keeps the items the lambda is true for |
WHERE |
OrderBy(p => ...) |
Sorts by what the lambda returns, smallest first | ORDER BY |
OrderByDescending(p => ...) |
Sorts largest first | ORDER BY ... DESC |
ThenBy(p => ...) |
Sorts items that tied in the sort before it | the second column of ORDER BY |
Select(p => ...) |
Turns each item into what the lambda returns | SELECT |
List<Product> menu = new List<Product>
{
new Product("Coffee", "Drinks", 3.50m),
new Product("Tea", "Drinks", 2.75m),
new Product("Juice", "Drinks", 4.00m),
new Product("Croissant", "Bakery", 3.75m),
new Product("Muffin", "Bakery", 2.50m),
new Product("Bagel", "Bakery", 2.25m)
};
List<string> cheapest = menu
.Where(p => p.Price < 3.00m)
.OrderBy(p => p.Price)
.Select(p => quot;{p.Name} {p.Price:C}")
.ToList();
foreach (string line in cheapest)
{
Console.WriteLine(line);
}
Console.WriteLine();
foreach (Product p in menu.OrderBy(p => p.Category).ThenByDescending(p => p.Price))
{
Console.WriteLine( quot;{p.Category}: {p.Name}");
}
public class Product
{
public string Name { get; }
public string Category { get; }
public decimal Price { get; }
public Product(string name, string category, decimal price)
{
Name = name;
Category = category;
Price = price;
}
}
Bagel $2.25
Muffin $2.50
Tea $2.75
Bakery: Croissant
Bakery: Muffin
Bakery: Bagel
Drinks: Juice
Drinks: Coffee
Drinks: Tea
Select can turn each product into anything: here, a line of text. Select(p => p.Name) would give just the names, and Select(p => p.Price * 1.1m) the prices with 10% added.
Summarize
These end a chain with one value:
| Method | Returns |
|---|---|
Count(), Count(p => ...) |
How many items, or how many the lambda is true for |
Sum(p => ...), Average(p => ...) |
The total, or the average, of what the lambda returns |
Min(p => ...), Max(p => ...) |
The smallest, or the largest, of what the lambda returns |
Any(p => ...) |
true if the lambda is true for at least one item |
All(p => ...) |
true if the lambda is true for every item |
List<Product> menu = new List<Product>
{
new Product("Coffee", "Drinks", 3.50m),
new Product("Tea", "Drinks", 2.75m),
new Product("Juice", "Drinks", 4.00m),
new Product("Croissant", "Bakery", 3.75m),
new Product("Muffin", "Bakery", 2.50m),
new Product("Bagel", "Bakery", 2.25m)
};
Console.WriteLine(menu.Count(p => p.Category == "Drinks"));
Console.WriteLine(menu.Sum(p => p.Price));
Console.WriteLine(menu.Average(p => p.Price));
Console.WriteLine(menu.Max(p => p.Price));
Console.WriteLine(menu.Any(p => p.Price > 5.00m));
Console.WriteLine(menu.All(p => p.Price > 2.00m));
public class Product
{
public string Name { get; }
public string Category { get; }
public decimal Price { get; }
public Product(string name, string category, decimal price)
{
Name = name;
Category = category;
Price = price;
}
}
3
18.75
3.125
4.00
False
True
GroupBy
GroupBy sorts the items into groups by what the lambda returns, like SQL's GROUP BY. Each group has a Key (what its items share) and is itself a collection, so you can count and total it:
List<Product> menu = new List<Product>
{
new Product("Coffee", "Drinks", 3.50m),
new Product("Tea", "Drinks", 2.75m),
new Product("Juice", "Drinks", 4.00m),
new Product("Croissant", "Bakery", 3.75m),
new Product("Muffin", "Bakery", 2.50m),
new Product("Bagel", "Bakery", 2.25m)
};
foreach (var group in menu.GroupBy(p => p.Category))
{
Console.WriteLine( quot;{group.Key}: {group.Count()} items, from {group.Min(p => p.Price):C}");
}
public class Product
{
public string Name { get; }
public string Category { get; }
public decimal Price { get; }
public Product(string name, string category, decimal price)
{
Name = name;
Category = category;
Price = price;
}
}
Drinks: 3 items, from $2.75
Bakery: 3 items, from $2.25
(A group's full type is a mouthful, IGrouping<string, Product>, so var is the usual choice here. The groups come out in the order their first item appears.)
Pick items
| Method | Returns |
|---|---|
First(p => ...) |
The first item the lambda is true for. Throws an InvalidOperationException if there's none. |
FirstOrDefault(p => ...) |
The same, but null (or 0, for numbers) if there's none |
Take(n), Skip(n) |
The first n items, or everything after the first n |
Distinct() |
The items with duplicates removed |
List<Product> menu = new List<Product>
{
new Product("Coffee", "Drinks", 3.50m),
new Product("Tea", "Drinks", 2.75m),
new Product("Juice", "Drinks", 4.00m),
new Product("Croissant", "Bakery", 3.75m),
new Product("Muffin", "Bakery", 2.50m),
new Product("Bagel", "Bakery", 2.25m)
};
Console.WriteLine(menu.First(p => p.Category == "Bakery").Name);
Product? soup = menu.FirstOrDefault(p => p.Name == "Soup");
Console.WriteLine(soup == null ? "No soup today" : soup.Name);
List<string> topTwo = menu.OrderByDescending(p => p.Price).Take(2).Select(p => p.Name).ToList();
Console.WriteLine(string.Join(", ", topTwo));
List<string> categories = menu.Select(p => p.Category).Distinct().ToList();
Console.WriteLine(string.Join(", ", categories));
public class Product
{
public string Name { get; }
public string Category { get; }
public decimal Price { get; }
public Product(string name, string category, decimal price)
{
Name = name;
Category = category;
Price = price;
}
}
Croissant
No soup today
Juice, Croissant
Drinks, Bakery
Use First when there must be a match (if there isn't, something's wrong, and the exception says so), and FirstOrDefault when "none" is a normal answer, then check for null. Skip and Take together give pages: Skip(10).Take(10) is page 2 of a list shown 10 at a time.
Check yourself
3 questions to try on the lesson page.
So what
Filter with Where, sort with OrderBy, reshape with Select, summarize with Count, Sum and GroupBy, and pick with First and Take. Those few methods replace most of the loops you'd otherwise write.