Even though we are in the alpha of .NET 9 and .NET 8 was released not more than two months ago, the dotnet team does not sleep and pushes new changes! In this blog post, we are checking what new methods were added to everyones favorite: LINQ.
CountBy
Many LINQ functions have a By extension, where you can provide a selector function to group the elements by. For example, MinBy
, MaxBy
, DistinctBy
, and so on. Now we have a new one: CountBy
. It groups the elements by the selector function and returns an enumeration of KeyValuePair
s. The key is the object, and the value is the count of the elements in the group.
public record Person(string FirstName, string LastName);
List<Person> people =
[
new("Steve", "Jobs"),
new("Steve", "Carell"),
new("Elon", "Musk")
];
foreach (var peopleWithSameFirstName in people.CountBy(p => p.FirstName))
{
Console.WriteLine($"There are {peopleWithSameFirstName.Value} people with the name {peopleWithSameFirstName.Key}");
}
This prints:
There are 2 people with the name Steve
There are 1 people with the name Elon
AggregateBy
This method is similar to CountBy
, but instead of counting the elements, it aggregates them. You can provide a seed and an aggregation function. The aggregation function gets the current aggregation and the current element as parameters. The aggregation function must return the new aggregation.
public record Person(string FirstName, string LastName, int SomeNumber);
List<Person> people =
[
new("Steve", "Jobs", 10),
new("Steve", "Carell", 100),
new("Elon", "Musk", 10)
];
var aggregateBy = people.AggregateBy(person => person.SomeNumber, x => 0, (x, y) => x + y.SomeNumber);
foreach (var kvp in aggregateBy)
{
Console.WriteLine($"Sum of SomeNumber for Key {kvp.Key} is {kvp.Value}");
}
This will print:
Sum of SomeNumber for key 10 is 20
Sum of SomeNumber for key 100 is 100
Index
Now this isn't necessarily really new, because you could do this as of today. Index
does return the item and the index of the item in the collection.
public record Person(string FirstName, string LastName);
List<Person> people =
[
new("Steve", "Jobs"),
new("Steve", "Carell"),
new("Elon", "Musk")
];
foreach (var (index, item) in people.Index())
{
Console.WriteLine($"Entry {index}: {item}");
}
This will print:
Entry 0: Person { FirstName = Steve, LastName = Jobs }
Entry 1: Person { FirstName = Steve, LastName = Carell }
Entry 2: Person { FirstName = Elon, LastName = Musk }
You could utilize Select
overload to do the same:
foreach (var (item, index) in people.Select((item, index) => (item, index)))
{
Console.WriteLine($"Entry {index}: {item}");
}
What bugs me the most is, that the order of index
and item
is reversed! But that was a concious decision by the dotnet team:
We decided to return Index first, Value second because it feels more natural despite the fact that the existing Select() method provides value first and index second.
Source: https://github.com/dotnet/runtime/issues/95563#issuecomment-1852656539