6 Improvements For LINQ in .NET 6

Despite the powerful capabilities of the C# programming language, Microsoft continues to improve its offspring. Today we will look at what features have appeared in LINQ (Language-Integrated Query). I will say right away that you will not see any breakthrough, mainly syntactic sugar, which speeds up the development process. The demonstration will take place immediately on the examples. I will show how it is done now and how it can be done using the new features of C# 10.

1. Split an array into arrays of equal sizes

Let’s assume I want to split the list of the Ukrainian cities into three arrays. And each array should contain a maximum of 3 cities.
 Old way: 

var cities = new []{"Kiev", "Kharkiv", "Kherson", "Odessa", "Dnepr", "Lviv", "Mariupol", "Simferopol", "Sevastopol", "Poltava"};
IEnumerable<IEnumerable<T>> SplitBy<T>(IEnumerable<T> source, int arraySize)
{
    return source.Select((x,i) => new {Index = i, Value = x})
        .GroupBy(x=>x.Index/arraySize)
        .Select(x=>x.Select(v=>v.Value));
}
var arrayOfArray = SplitBy<string>(cities, 3);
int i = 0;
foreach (var cityArray in arrayOfArray)
{
    i++;
    Console.WriteLine($"Array {i}:");
    foreach (var city in cityArray)
    {
        Console.WriteLine(city);
    }
    Console.WriteLine();
}
Console.ReadLine();

 

As you can see I created the SplitBy function which split our array into chunks of arrays with max size ‘arraySize’.

 New way: 

Using the Chunk method you can do the same thing easyly without the ‘SplityBy’ function.

var cities = new []{"Kiev", "Kharkiv", "Kherson", "Odessa", "Dnepr", "Lviv", "Mariupol", "Simferopol", "Sevastopol", "Poltava"};
var arrayOfCitiesNewWay = cities.Chunk(3);

chunk array example C#

2. TryGetNonEnumeratedCount – Improve Performance

The TryGetNonEnumeratedCount(IEnumerable source, out int count) method helps a lot in cases where you need to find out the number of elements in a collection without iterating over it. You know that when we execute the ‘Count()’ method on an IEnumerable it is first checked if it can be cast to an ICollection. If yes, then the count is provided immediately, otherwise, all the elements have to be looped through. The ‘TryGetNonEnumeratedCount’ method will return a true or false depending upon if the count can be returned without iterating the list of items. I guess a real use case could be with IQueryable where you need to iterate through all rows to find a number of elements. To avoid performance issues you can use the ‘TryGetNonEnumeratedCount’ method. In my opinion, the usage of this method is a bit rare. Check the example below. There are two usages of the ‘TryGetNonEnumeratedCount’ method. The first used for the array of strings will return a count of elements and the second for IQueryable which will not return a count of items.

using System.Collections;
using System.Linq.Expressions;
var cities = new []{"Kiev", "Kharkiv", "Kherson", "Odessa", "Dnepr", "Lviv", "Mariupol", "Simferopol", "Sevastopol", "Poltava"};
var myDbCities = new MyDbObject<string>();
int citiesCount;
int dbMyDbCitiesCount;
if (cities.TryGetNonEnumeratedCount(out citiesCount))
{
    Console.WriteLine($"Cities Count:{citiesCount}");
}
if(myDbCities.TryGetNonEnumeratedCount(out dbMyDbCitiesCount))
{
    Console.WriteLine($"MyDbCities Count:{dbMyDbCitiesCount}");
}
else
{
    Console.WriteLine("Count could not be calculated for IEnumerable");
}
Console.ReadKey();
class MyDbObject<T> : IQueryable<T>
{
    public Type ElementType => throw new NotImplementedException();
    public Expression Expression => throw new NotImplementedException();
    public IQueryProvider Provider => throw new NotImplementedException();
    public IEnumerator<T> GetEnumerator()
    {
        throw new NotImplementedException();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

TryGetNonEnumeratedCount example

3. Zip – Combine Two or More Arrays

The Zip operator merges the corresponding elements of two sequences using a specified selector function.

var bugs = new[] { "Bug1", "Bug2", "Bug3", "Bug4" };
var bugStatuses = new[] { "Fixed", "Inprogress", "Fixed", "Cancelled" };
IEnumerable<(string bug, string bugStatus)> bugAndStatuses = bugs.Zip(bugStatuses);
foreach (var item in bugAndStatuses)
    Console.WriteLine(item);
Console.ReadKey();

 

Example:
zip C# example

4. MinBy and MaxBy Methods

These two methods return the maximum/minimum value in a generic sequence according to a specified key selector function and key comparer.
Here is an example. Let’s find the product with max and min price.

var products = new List<Product>()
{
    new Product { Name = "MyProduct1", Price = 50},
    new Product { Name = "MyProduct2", Price = 10},
    new Product { Name = "MyProduct3", Price = 7}
};
 Old way: 
var expensiveProductOld = products.
    OrderBy(x => x.Price).First();
var cheapProductOld = products.
    OrderByDescending(x => x.Price).First();
 New way: 
var expensiveProduct = products.MaxBy(x => x.Price);
var cheapProduct = products.MinBy(x => x.Price);

 

Max will be MyProduct1 with 50 and min will be MyProduct3 with 7.

5. Index Support – Get Element From the End of Array

Using the  ^  you can take an element from the end of the array.

var cities = new[] { "Kiev", "Kharkiv", "Kherson", "Odessa", "Dnepr", "Lviv", "Sevastopol", "Poltava" };
var thirdFromTheEnd = cities.ElementAt(^3);

The ‘thirdFromTheEnd’ will be ‘Lviv’.

6. Range Support for Take

This feature allows to index/slice collections at runtime. For example, if you need to take 3 elements starting from index 2.
 Old way 

var cities = new[] { "Kiev", "Kharkiv", "Kherson", "Odessa", "Dnepr", "Lviv", "Mariupol", "Simferopol", "Sevastopol", "Poltava" };
var myThreeCitiesOld = cities.Skip(2).Take(3);

 

 New way 
var cities = new[] { "Kiev", "Kharkiv", "Kherson", "Odessa", "Dnepr", "Lviv", "Mariupol", "Simferopol", "Sevastopol", "Poltava" };
var myFourCities = cities.Take(2..5);

 
Both variants will return ‘Kherson’, ‘Odessa’ and ‘Dnepr’.
Additional you can use the next statements:

var lastTwoCities = cities.Take(^2..);
var skipLastOne = cities.Take(..^1)

Leave a Comment