18
.NET 6 LINQ New Features
.NET 6
is the upcoming major overhaul for .NET
. It unifies entire .NET
experience. No more .NET Core
, .NET Full Framework
, Xamarin
, Mono
, etc. Just a single .NET
.
At the time of writing this post, 5 previews of .NET 6
have been released and last couple of releases have been blessing for LINQ
.
Here are few top new LINQ
features:
- New methods
MaxBy
andMinBy
- New methods
Chunk
- New methods
DistinctBy
,UnionBy
,IntersectBy
, andExceptBy
-
Index
andRange
parameters - New method
TryGetNonEnumeratedCount
- Default parameters for
FirstOrDefault
,LastOrDefault
, andSingleOrDefault
-
Zip
supports 3IEnumerables
Finding out maximum or minimum has been easier than even with these new MaxBy
or MinBy
methods.
Suppose a List
of Person
s as:
static List<Person> people = new List<Person>
{
new Person { Id = 1, Name = "Amit", Age = 38},
new Person { Id = 2, Name = "Ravi", Age = 36},
new Person { Id = 3, Name = "Manish", Age = 34},
new Person { Id = 4, Name = "Satish", Age = 29},
};
If we need to get maximum and minimum, currently here's how to do it:
//Without using MaxBy and MinBy
Person oldestPerson = people.OrderByDescending(person => person.Age).First();
Person youngestPerson = people.OrderBy(person => person.Age).First();
Console.WriteLine($"Oldest Person without using MaxBy: {oldestPerson.Name}");
Console.WriteLine($"Youngest Person without using MaxBy: {youngestPerson.Name}");
But with MaxBy
and MinBy
, it's just a single method call:
Person oldestPerson = people.MaxBy(person => person.Age);
Person youngestPerson = people.MinBy(person => person.Age);
Console.WriteLine($"Oldest Person using MaxBy: {oldestPerson.Name}");
Console.WriteLine($"Youngest Person using MaxBy: {youngestPerson.Name}");
Neat, right? Let me know in the comments, if you have alternate methods of getting maximum and minimum using LINQ
.
A new method Chunk
slices the IEnumerable
into provided sizes. e.g. If you have a collection of 4 elements and you need to cluster them into fixed size of 2, here's how to do:
IEnumerable<Person[]> cluster = people.Chunk(2);
// Print each cluster.
foreach(var people in cluster)
{
Console.WriteLine($"Cluster of {string.Join(",", people.Select(person => person.Name))}");
}
//Prints
// Cluster of Amit,Ravi
// Cluster of Manish,Satish
Exisiting set methods Distinct
, Union
, Intersect
, and Except
have been powered up by these new methods which can take a selector function to operate.
e.g.
IEnumerable<Person> evenAgedPeople = people.Where(person => person.Age % 2 == 0);
//Amit,Ravi,Manish
IEnumerable<Person> personAbove35 = people.Where(person => person.Age > 35);
//Amit,Ravi
IEnumerable<Person> union = evenAgedPeople.UnionBy(personAbove35, x => x.Age);
//Amit,Ravi,Manish
IEnumerable<Person> intersection = evenAgedPeople.IntersectBy(personAbove35.Select(p => p.Age), x => x.Age);
//Amit,Ravi
Let me know in the comments, the usage of DistinctBy
and ExceptBy
.
Range
: ..
, and Index
: ^
already exist in C#
8, .NET 6
brings these two to LINQ
.
- The
ElementAt
operator now takes indices from the end.
Person secondLastPerson = people.ElementAt(^2);
//Manish
-
Skip
andTake
now takeRange
as well:
IEnumerable<Person> take3People = people.Take(..3);
//Amit,Ravi,Manish
IEnumerable<Person> skip1Person = people.Take(1..);
//Ravi,Manish,Satish
IEnumerable<Person> take3Skip1People = people.Take(1..3);
//Ravi,Manish
IEnumerable<Person> takeLast2People = people.Take(^2..);
//Manish,Satish
IEnumerable<Person> skipLast3People = people.Take(..^3);
//Amit
IEnumerable<Person> takeLast3SkipLast2 = people.Take(^3..^2);
//Ravi
Sometimes you need to get a count without the enumeration. TryGetNonEnumeratedCount
will try to get count without forcing an enumeration. It internally checks for the implementation of ICollection
i.e. IEnumerable
that already has a mechanism to get count without forcing an enumeration. Otherwise it'll try to take advantage of new improvements in LINQ
.
Useful in scenarios where you want to get a count but not at the cost of enumerating the IEnumerable
.
e.g.
List<Person> anotherList = people.TryGetNonEnumeratedCount(out int count) ? new List<Person>(count): new List<Person>();
anotherList.AddRange(people);
In our case right now, we know that people is just an in-memory list, but what if it was from database of some Stream
, then it would've made sense to find out the count to optimize the instantiation of anotherList
object by specifying capacity
parameter.
Current FirstOrDefault
, LastOrDefault
, and SingleOrDefault
methods return default(T)
if the source IEnumerable
is empty. The new overloads accept a parameter which will be returned if source is empty.
e.g.
List<int> emptyList = new List<int>();
int value = emptyList.FirstOrDefault(-1);
//-1 instead of 0
This reminds me of ISNULL
function of SQL Server
.
Before .NET 6
, Zip
used to take only 2 parameters. Now it takes 3 parameters:
IEnumerable<int> ids = Enumerable.Range(1, 4);
IEnumerable<Person> allPeople = people;
IEnumerable<int> allAges = people.Select(person => person.Age);
IEnumerable<(int Id, Person Person, int Age)> zipped = ids.Zip(allPeople, allAges);
.NET 6
previews are bring awesome set of new features with every new release. Let me know in comments which all features you're excited for.
- Download
.NET 6
today and try out. - Source code is available at: https://github.com/iSatishYadav/.net-6-linq-new-features
Originally published at my blog at: https://blog.satishyadav.com/.net-6-linq-new-features
18