按多个条件分组列表,LINQ

Group list by multiple conditions, LINQ

我有 Book class:

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

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,
        },
    };

现在我想按价格对列表进行分组,例如便宜、中等、昂贵:

例如,便宜 = 10,中等 = 20,昂贵 = 30 分组列表如下:

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}},
    } 

更新

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

我可以得到分组的图书列表,但是不能按多个条件分组。

更新

感谢大家的快速回答,看来 DotNet Developer 的回答简短而贴心。

books.GroupBy(book => book.Price <= cheap ? cheap :
                    (book.Price > cheap && book.Price <= middle ? middle : expensive))
         .Select(group => (group.Key, group.ToList()))

GroupBy 运算符需要 return 作为组键的函数或表达式。您可以根据价格使用此 return 组“名称”:

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

结果是包含每个类别及其内容的字典:

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}]
}

我在 GroupBy 中使用了模式匹配来适应单个 lambda 中的所有条件。不过,这可能是一个单独的功能:

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);

首先 - 将类别添加到列表

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

然后按此类别分组:

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);
}

类似的东西?

使用三元条件运算符

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

如果您不一定受特定类别值的约束,而是希望按可被 10 整除的价格对图书进行分组(即 10203040 等),您可以通过将 Price 四舍五入到最接近的 10 并将其用作您的 group-by-value.

这是一个例子,使用 Math.Ceiling() 进行舍入:

Math.Ceiling(value / 10) * 10 // Round 'value' upwards to nearest 10

应用为 group-by-condition:

List<(decimal Category, List<Book> Books)> groupedBooks = books
    .GroupBy(book => Math.Ceiling(book.Price / 10) * 10,
        (category, booksInCategory) => ( 
            Category: category, 
            Books: booksInCategory.ToList() ))
    .OrderBy(gr => gr.Category)
    .ToList();

使用您为 books 提供的示例列表,生成的 groupedBooks 包含:

Category: 10
Book: { Id: 4 Name: DD Price: 9.0000 }

Category: 20
Book: { Id: 1 Name: AA Price: 19.0000 }
Book: { Id: 2 Name: BB Price: 18.0000 }

Category: 30
Book: { Id: 3 Name: CC Price: 30.0000 }

示例 fiddle here.