【问题标题】:Nested Group by LINQ to Nested object通过 LINQ 嵌套组到嵌套对象
【发布时间】:2018-07-20 06:23:15
【问题描述】:

我无法使用 LINQ 查询解决此问题。

所以我们有Book的表结构如下:

LibraryId || LibraryName || ShelveId || ShelveName || Cost || Name || ForeName || Stuff

我想先按图书馆分组,然后按书架分组。图书馆是书架的清单,书架是书的清单。性能很重要。这是真实数据的片段。

var table = new[] {
    new Book (1, "Green", 42, "A", 10, "Gra", "Bar", "etc."),
    new Book (1, "Green", 43, "B", 21, "Grb", "Bar", "etc."),
    new Book (2, "Blue", 652, "C", 10, "Blc", "Bar", "etc."),
    new Book (2, "Blue", 652, "C", 01, "Bl2", "Bar", "etc."),
    new Book (2, "Blue", 123, "D", 12, "Bld", "Bar", "etc."),
    new Book (8, "White", 94, "E", 14, "Foo", "Bar", "etc."),
    new Book (9, "Grey", 142, "F", 11, "Foo", "Bar", "etc."),
    new Book (9, "Grey", 142, "F", 12, "Bar", "Bar", "etc.")
};

类:

class Book
{
    public int LibraryId {get;set;}
    public string LibraryName {get;set;}

    public int ShelveId {get;set;}
    public string ShelveName {get;set;}

    public int Cost {get;set;}
    public string Name {get;set;}
    public string ForeName {get;set;}
    public string Stuff {get;set;}

    public Book(int libraryId, string libraryName, int shelveId, string shelveName
                , int cost, string name, string foreName, string stuff)
    {
        LibraryId = libraryId;
        LibraryName = libraryName;
        ShelveId = shelveId;
        ShelveName = shelveName;
        Cost = cost;
        Name = name;
        ForeName = foreName;
        Stuff = stuff;
    }
}

class Library
{
    public int Id {get;set;}
    public string Name {get;set;}
    public int Cost {get;set;}
    public List<Shelve> Shelves {get;set;}

    public Library (Shelve shelve)
    {
        Id = shelve.Books[0].LibraryId;
        Name = shelve.Books[0].LibraryName;
        Cost = shelve.Cost;
        Shelves = new List<Shelve> {shelve};
    }
}

class Shelve
{
    public int Id {get;set;}
    public string Name {get;set;}
    public int Cost {get;set;}
    public List<Book> Books {get;set;}

    public Shelve (Book book)
    {
        Id = book.ShelveId;
        Name = book.ShelveName;
        Cost = book.Cost;
        Books = new List<Book> {book};
    }
}

我用一个很好的老foreach来实现我的行为:

var libraries = new List<Library> { new Library (new Shelve(table[0])) };
foreach (var item in table.Skip(1))
{
    if (item.LibraryId != libraries.Last().Id)
    {
        libraries.Add(new Library(new Shelve(item)));
        continue;
    }

    if (item.ShelveId != libraries.Last().Shelves.Last().Id)
    {
        libraries.Last().Cost += item.Cost;
        libraries.Last().Shelves.Add(new Shelve(item));
        continue;
    }

    libraries.Last().Cost += item.Cost;
    libraries.Last().Shelves.Last().Cost += item.Cost;
    libraries.Last().Shelves.Last().Books.Add(item);
}
var total = libraries.Sum(x => x.Cost);

据我所知,这是一个 O(n) 的解决方案。

现在,我想重构它。我想我们可以用GroupBy 做点什么。我试过这个:

var grouped = table
    .GroupBy(l => new { l.ShelveId, l.LibraryId})
    .GroupBy(l => l.Key.LibraryId);

foreach(var country in grouped)
{
    foreach(var state in country)
    {
        foreach(var personInState in state)
        {
            Console.WriteLine(personInState.Name);
        }
    }
}

但我不知道如何汇总成本并将 LibraryName 和 ShelveName 添加到每个 LibraryShelve

Try it Online!(带有基本测试)

【问题讨论】:

  • 能否编辑您的问题并粘贴 Library、Shelve 和 Book 的类定义?
  • @RuiJarimba 完成。
  • LibraryShelf(不是 Shelve,这是一个动词)真的不应该包含 Cost 属性:如果我们将它们视为数据库中的表,这会破坏规范化规则。如果您愿意省略它,您可以使用嵌套查询(请参阅here)。

标签: c# performance linq refactoring


【解决方案1】:

也许您可以通过使用如下计算的属性来简化它

class Shelve
{
    public int Id {get;set;}
    public string Name {get;set;}
    public List<Book> Books {get;set;} = List<Book>();
    public int Cost 
    {
        get{ return this.Books.Sum(x => x.Cost); } 
    }
}

你可以对Library类做同样的事情......

class Library
{
    public int Id {get;set;}
    public string Name {get;set;}
    public List<Shelve> Shelves {get;set;} = new List<Shelve>();
    public int Cost 
    {
        get{ return this.Shelves.Sum(x => x.Cost); } 
    }
}

我认为它可以使您的业务逻辑代码更加清晰,并且您无需使用复杂的 group by 表达式来敲头

【讨论】:

  • 确实有效。但是我们不是把总和称为很多时间吗?当我做Library.Cost 时,我会走每一个Shelve 调用他们的Cost,这会触发每本书的每一个Cost 的另一个Sum waling,不是吗?
  • @aloisdg 我实际上错过了您问题的“性能问题”部分。对此表示歉意。我根本没有对这种方法进行任何性能分析......所以,我无法回答这部分问题。我会留下答案,以防万一它对其他人有帮助
  • 是的,当然。尽管如此,这是一个干净的答案:)
猜你喜欢
  • 2020-03-11
  • 2021-10-27
  • 1970-01-01
  • 2021-09-03
  • 2021-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-17
相关资源
最近更新 更多