# What's New in .NET 9 - LINQ Updates

> With the release of .NET, LINQ has some new features. In this post, we'll look at CountBy, AggregateBy, and Index.

- URL: https://blog.nimblepros.com/blogs/whats-new-in-dotnet-9-linq-updates/
- Author: [Sarah Dutkiewicz](https://blog.nimblepros.com/authors/sadukie/)
- Published: 2024-11-26
- Categories: [Software Development](https://blog.nimblepros.com/categories/software-development/)
- Publisher: [NimblePros](https://blog.nimblepros.com/)

---

LINQ (Language Integrated Query) has always been a game-changer for .NET developers, streamlining data manipulation with elegance and efficiency. With .NET 9, LINQ is leveling up with exciting new methods like `CountBy`, `AggregateBy`, and `Index`. These additions promise to make your data queries as strategic as a well-played game of Settlers of Catan. In this post, we'll explore how these enhancements can bring order to your data chaos, just like a winning strategy brings victory in your favorite board games.

## Starting Data

For our demos, we'll have a collection of games with their names, genres, and prices.

We have a `Game` type with a name, genre, and price:

```csharp
class Game
{
    public string Name { get; set; }
    public string Genre { get; set; }
    public decimal Price { get; set; }    
}
```

This is our collection of games:

```csharp
 var games = new List<Game>
        {
            new Game { Name = "Werewolf", Genre = "Party", Price = 9.99m },
            new Game { Name = "Splendor", Genre = "Strategy", Price = 34.99m },
            new Game { Name = "Azul", Genre = "Abstract", Price = 29.99m },
            new Game { Name = "Bang", Genre = "Party", Price = 19.99m },
            new Game { Name = "Bärenpark", Genre = "Tile Placement", Price = 39.99m },
            new Game { Name = "Forbidden Planet", Genre = "Co-op", Price = 24.99m },
            new Game { Name = "Catan", Genre = "Strategy", Price = 44.99m },
            new Game { Name = "Ticket to Ride", Genre = "Strategy", Price = 49.99m },
            new Game { Name = "Hive", Genre = "Abstract", Price = 24.99m },
            new Game { Name = "Spyfall", Genre = "Party", Price = 14.99m },
            new Game { Name = "Spirit Island", Genre = "Co-op", Price = 59.99m },
            new Game { Name = "Draftosaurus", Genre = "Family", Price = 19.99m },
            new Game { Name = "7 Wonders", Genre = "Strategy", Price = 39.99m }
        };
```

Let's see the new LINQ methods from .NET 9 in action!

## CountBy

When it comes to counting by attribute or category, `CountBy` can assist with that! Consider this example, where we're counting the number of games by their genre. This code returns a collection of `<string, int>`.

```csharp
// Use CountBy to count games by genre
var gamesByGenre = games
    .Select(game => game.Genre)
    .CountBy(genre => genre);

// Output the results
Console.WriteLine("Games by genre:");
foreach (var (genre, count) in gamesByGenre)
{
    Console.WriteLine($"{genre}: {count}");
}
```

In this code, we have our collection of games, use `Select` to get just the genre, and use `CountBy` to get those counts.  The output is this:

```text
Games by genre:
Party: 3
Strategy: 4
Abstract: 2
Tile Placement: 1
Co-op: 2
Family: 1
```

🤔 I think we might like our strategy games.

If you find yourself analyzing collections and wanting to get counts for particular groups, `CountBy` can assist with that.

## AggregateBy

When it comes to calculating values over a collection, `AggregateBy` can assist with this.

Consider this example where we are getting the sum of the costs by genre and sort it by most expensive to least expensive. In this example, we are seeding at 0 to be explicit. 

```csharp
// Use AggregateBy to calculate average price per genre
var totalPriceByGenre = games
    .AggregateBy(
        keySelector: game => game.Genre,
        seed: 0m,
        (totalValue, currentGame) => totalValue + currentGame.Price  // Aggregation function
    )
    .OrderByDescending(genrePrices => genrePrices.Value);

// Output the results
Console.WriteLine("Total price by genre:");
foreach (var (genre, price) in totalPriceByGenre)
{
    Console.WriteLine($"{genre}: ${price:F2}");
}
```

The output for this code is:

```text
Total price by genre:
Strategy: $169.96
Co-op: $84.98
Abstract: $54.98
Party: $44.97
Tile Placement: $39.99
Family: $19.99
```

So where are we spending the most money? 💸 Strategy games!

## Index

With `Index`, we can now get the index collection for an `IEnumerable`. When calling `Index`, it returns a tuple of `(int, TSource)`.  Suppose we want to create a list of our games from most expensive to least expensive and then alphabetically within the price. Let's display it as a numbered list. Keep in mind that we're looked at weirdly for starting our lists at 0, so we'll start our list numbering at 1.

We will use LINQ's `OrderByDescending` and `ThenBy` to order our `List<Game>` to achieve our ordering goal. Then, we will chain an `Index` call to get the index for that ordered collection's results.

The `foreach`'s individual item is strongly typed in this demo so that you can see that, in our case, the `Index` call will return a tuple of `(int index, Game game)`.

This code can achieve that:

```csharp
IEnumerable<(int Index, Game Item)> indexedMostExpensiveGames = games
    .OrderByDescending(game => game.Price)
    .ThenBy(game => game.Name)
    .Index();

// Output the results
Console.WriteLine("Games - Most to Least Expensive:");
// Explicitly typing the tuple returned in the Index()
foreach ((int index, Game game) orderedGameEntry in indexedMostExpensiveGames)
{
    Console.WriteLine($"{orderedGameEntry.index + 1}. {orderedGameEntry.game.Name} - ${orderedGameEntry.game.Price:F2}");
}
```

This is the output:

```text
Games - Most to Least Expensive:
1. Spirit Island - $59.99
2. Ticket to Ride - $49.99
3. Catan - $44.99
4. 7 Wonders - $39.99
5. Bärenpark - $39.99
6. Splendor - $34.99
7. Azul - $29.99
8. Forbidden Planet - $24.99
9. Hive - $24.99
10. Bang - $19.99
11. Draftosaurus - $19.99
12. Spyfall - $14.99
13. Werewolf - $9.99
```

Keep in mind that the `index` provided by `Index` starts at 0.

## Conclusion

.NET 9's LINQ updates are like upgrading your game pieces from cardboard to beautifully crafted miniatures—they take something you already love and make it even better. With `CountBy`, `AggregateBy`, and `Index`, you can write queries that feel as smooth as sliding your token across a pristine game board. Ready to play? Grab your tokens (or, in this case, your IDE), and dive into the strategic world of LINQ enhancements!

## References

- [Enumerable.CountBy reference documentation](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.countby?view=net-9.0)
- [Enumerable.AggregateBy reference documentation](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.aggregateby?view=net-9.0)
- [Enumerable.Index reference documentation](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.index?view=net-9.0)
