Industry Insights, Information, and Developer News
I've been writing C# for decades and I still discover LINQ methods that make me think "where have you been all my life?" Just last week, I was doing a code review and saw someone write a 10-line loop that could've been replaced with a single LINQ call.
LINQ has been around since 2007 but there are still a bunch of methods that fly under the radar. Sure, everyone knows Where(), Select(), and FirstOrDefault(). But there's a whole pile of other methods that can save you time and make your code cleaner.
Where()
Select()
FirstOrDefault()
Let's dig into some LINQ methods that you probably aren't using but definitely should be.
Chunk() - Batch Processing Made Easy I don't know about you but I constantly need to process collections in batches. Maybe it's sending emails 25 at a time to avoid rate limits, or processing database records in chunks to avoid timeouts. Before .NET 6, you'd write something like this:
public void ProcessInBatches(List<Customer> customers, int batchSize) { for (int i = 0; i < customers.Count; i += batchSize) { var batch = customers.Skip(i).Take(batchSize).ToList(); SendEmailBatch(batch); } }
With Chunk(), this becomes:
Chunk()
public void ProcessInBatches(List<Customer> customers, int batchSize) { foreach (var batch in customers.Chunk(batchSize)) { SendEmailBatch(batch); } }
Way cleaner, right? And Chunk() returns arrays, which is more efficient than the Skip/Take approach.
DistinctBy() and UnionBy() - Finally! For years, getting distinct objects by a property meant either implementing IEqualityComparer or doing some GroupBy gymnastics. Check out this old-school approach:
IEqualityComparer
GroupBy
// The old way - grouping and taking first var uniqueCustomers = customers .GroupBy(c => c.Email) .Select(g => g.First()) .ToList();
Now with DistinctBy():
DistinctBy()
// The new way - clean and obvious var uniqueCustomers = customers.DistinctBy(c => c.Email).ToList();
Same deal with UnionBy(). If you want to combine two lists and remove duplicates based on a property:
UnionBy()
var allCustomers = onlineCustomers .UnionBy(inStoreCustomers, c => c.CustomerId) .ToList();
MinBy() and MaxBy() - No More Sorting! Here's something I used to see all the time - sorting an entire collection just to get the min or max item:
// Please don't do this var oldestCustomer = customers .OrderByDescending(c => c.Age) .FirstOrDefault();
That sorts the entire collection (O(n log n)) when you just need one item! Use MaxBy() instead:
MaxBy()
// Much better - O(n) operation var oldestCustomer = customers.MaxBy(c => c.Age);
You also get MinBy() for the opposite direction. These methods return the actual object, not just the min/max value.
MinBy()
ExceptBy() and IntersectBy() - Set Operations on Objects Let's say you have a list of customers and you want to find which ones haven't placed orders. The old way involved extracting IDs and doing lookups:
// The old way var customerIds = customers.Select(c => c.Id).ToHashSet(); var orderCustomerIds = orders.Select(o => o.CustomerId).ToHashSet(); var inactiveIds = customerIds.Except(orderCustomerIds); var inactiveCustomers = customers.Where(c => inactiveIds.Contains(c.Id));
With ExceptBy():
ExceptBy()
// The new way var inactiveCustomers = customers .ExceptBy(orders.Select(o => o.CustomerId), c => c.Id);
IntersectBy() works the same way for finding items that exist in both collections.
IntersectBy()
TryGetNonEnumeratedCount() - Avoiding Multiple Enumeration This one's a bit more advanced but super useful. Sometimes you need to check if you can get the count of a collection without enumerating it. This is especially helpful when working with database queries or other expensive operations:
public void ProcessItems(IEnumerable<Order> orders) { // Try to get count without enumeration if (orders.TryGetNonEnumeratedCount(out int count)) { // We got the count "for free" - the source was a List, array, etc. Console.WriteLine($"Processing {count} orders"); } else { // It's a true enumerable (maybe from yield return or LINQ query) Console.WriteLine("Processing orders..."); } foreach (var order in orders) { ProcessOrder(order); } }
If the source is a List or array, it returns the count immediately. If it's a true enumerable (like from yield return), it returns false and you handle it accordingly.
List
yield return
Zip() - Combining Parallel Collections Ever need to combine two collections element by element? Maybe you have a list of products and a list of prices that you need to combine:
// The old way with indexes var products = GetProducts(); var prices = GetPrices(); var productPrices = new List<ProductPrice>(); for (int i = 0; i < products.Count && i < prices.Count; i++) { productPrices.Add(new ProductPrice { Product = products[i], Price = prices[i] }); }
With Zip():
var productPrices = products .Zip(prices, (product, price) => new ProductPrice { Product = product, Price = price }) .ToList();
.NET 6 also added an overload that produces tuples automatically:
// Creates tuples of (product, price) var pairs = products.Zip(prices); foreach (var (product, price) in pairs) { Console.WriteLine($"{product.Name}: ${price}"); }
Real-World Example: Data Processing Pipeline Let me show you how these methods work together. Let's say you're processing customer orders from multiple sources:
public void ProcessMonthlyOrders() { var onlineOrders = GetOnlineOrders(); var phoneOrders = GetPhoneOrders(); // Combine orders, removing duplicates by order ID var allOrders = onlineOrders.UnionBy(phoneOrders, o => o.OrderId); // Get unique customers who ordered var activeCustomers = allOrders .DistinctBy(o => o.CustomerId) .Select(o => GetCustomer(o.CustomerId)); // Find the highest value order var biggestOrder = allOrders.MaxBy(o => o.TotalAmount); // Process in batches of 50 foreach (var batch in allOrders.Chunk(50)) { ProcessOrderBatch(batch); } // Find customers who didn't order this month var allCustomers = GetAllCustomers(); var inactiveCustomers = allCustomers .ExceptBy(activeCustomers.Select(c => c.Id), c => c.Id); SendReactivationEmails(inactiveCustomers); }
Summary LINQ keeps evolving and it's worth checking what's new with each .NET release. These methods aren't just syntactic sugar -- they're more readable, often more performant, and definitely less error-prone than hand-rolled alternatives.
Next time you find yourself writing a loop to process a collection, ask yourself: is there a LINQ method for this? The answer is probably yes.
The methods I covered here shipped with .NET 6 and later, so if you're on an older version, you might need to upgrade. But honestly, if you're still on .NET Framework or an old .NET Core version, you're missing out on a lot more than just these LINQ methods.
About the Author
Posted by Benjamin Day on 09/24/2025