Home > Net >  Group list by multiple conditions, LINQ
Group list by multiple conditions, LINQ

Time:06-01

I have Book class:

public class Book
{
    public int Id {get; set;}
    public string Name {get; set;}
    public deciaml Price {get; set;}
}

And List of books:

List<Book> books = new List<Book>
    {
        new Book
        {
            Id = 1,
            Name = "AA",
            Price = 19.0000M
        },
        new Book
        {
            Id = 2,
            Name= "BB",
            Price = 18.0000M
        },
        new Book
        {
            Id = 3,
            Name = "CC",
            Price = 30.0000M
        },
        new Book
        {
            Id = 4,
            Name = "DD",
            Price = 9.0000M,
        },
    };

Now I want to group list by price rate, like cheap, medium, expensive:

for example, cheap = 10, medium = 20, expensive = 30 grouped list would be like:

List<(decimal category, List<Book> books)

    {
       category: 10
       books: List<Book>() {Book {4, "DD", 9.0000M}},
       category: 20
       books: List<Book>() {Book {1, "AA", 19.0000M}, Book {2, "BB", 18.0000M}},
       category: 30
       books: List<Book>() {Book {3, "CC", 30.0000M}},
    } 

UPDATE

books.GroupBy(book => book.Price).Select(group => (group.Key, group.ToList()));

I can get grouped list of books but, can not group by multiple conditions.

CodePudding user response:

The GroupBy operator expects a function or expression that returns the group key. You can use this to return the group "name" based on the price :

var bins=books.GroupBy(b=> b.Price switch {  
                          <=10          =>"cheap", 
                          >10 and <= 20 => " medium", 
                          _             => "expensive"})
              .ToDictionary(g=>g.Key,g=>g);

The result is a dictionary with each category and its contents :

Console.WriteLine(JsonSerializer.Serialize(bins));
-------
{
    " medium":[{"Id":1,"Name":"AA","Price":19.0000},{"Id":2,"Name":"BB","Price":18.0000}],
    "expensive":[{"Id":3,"Name":"CC","Price":30.0000}],
    "cheap":[{"Id":4,"Name":"DD","Price":9.0000}]
}

I used pattern matching in GroupBy to fit all conditions in a single lambda. This could be a separate function though :

string BookToBins(Book b)
{

    if (b.Price <= 10) return "cheap";
    if (b.Price <= 20) return "medium";
    return "expensive";
}

...
var bins=books.GroupBy(BookToBins)
              .ToDictionary(g=>g.Key,g=>g);

CodePudding user response:

First - add category to the list

var newBookList = BookList.Select(x=> new { Book = x, 
                                     Category = x.Price <= 10 ? 10 : 
                                                x.Price <= 20 ? 20 : 
                                                30 } ).ToList();

then Group By this Category :

var GroupedBooks = newBookList.GroupBy(x=>x.Category);
foreach(var category in GroupedBooks.Keys) {
         Console.WriteLine("category : {0}", category);
         foreach(var b in GroupedBooks[Keys].Select(x=>x.Book))
          Console.Write("Book {0}, \"{1}\", {2}",b.Id, b.Name, b.Price);
}

something like that?

CodePudding user response:

With ternary conditional operator

books.GroupBy(b => b.Price < 10 ? 10 : (b.Price < 20 ? 20 : 30));
  • Related