按属性对对象列表进行分组,拆分为多个列表。

4

现有结构(由于系统要求无法更改)

class Statement
{
    string section;
    string category;
    string statement;
}

示例语句列表

section      category     statement
1            a            apple
1            a            banana
1            b            potato
2            c            car
2            c            bus
2            d            plane

问题

我有一个 List<Statement>,需要根据章节和类别进行拆分,并将其转换为以下(或类似)结构:

struct SectionCollection
{
    string sectionName {get{return categories[0].section;}}
    List<CategoryCollection> categories;
}

struct CategoryCollection
{
    string categoryName {get{return statements[0].category;}}
    List<Statement> statements;
}

因此,从List<Statement>中,我应该有一个List<SectionCollection>,其中包含一个List<CategoryCollection>,其中包含一个List<Statement>
因此,在上面的数据示例中,我将拥有
  • List<SectionCollection>
    • List<CategoryCollection>(内部)
      • List<Statement>(内部)
注意事项 可能存在一个语句或类别在不同的部分中相同——这些仍然需要属于不同的SectionCollections 尝试 我的当前尝试,它可以正常工作,直到最终在内部循环中抛出空异常。这是我一直在研究的问题之一,所以我知道这个“解决方案”可能看起来很困惑。
var sections = statements.GroupBy(x => x.section).Select(y => y.ToList()).ToList();
foreach(var section in sections)
{
     SectionCollection thisSection = new SectionCollection();
     var categories = section.GroupBy(x => x.category).Select(y => y.ToList()).ToList();

    foreach(var category in categories)
    {
        thisSection.categories.Add(new CategoryCollection({statements = category});
    }
}

我没有看到 thisSection.categories 被初始化为 new - MFisherKDX
@MFisherKDX,那可能就是问题所在了。我正在研究这个问题,但我感觉我已经尝试过这个方法了,只是(愚蠢地)没有在我提供的示例中包含它。 - Bejasc
2个回答

2
你遇到空引用错误的原因是因为你没有在SectionCollection中初始化Categories列表。

更改后:

SectionCollection thisSection = new SectionCollection();

致:

SectionCollection thisSection = new SectionCollection() 
{ 
    Categories = new List<CategoryCollection>() 
};

将修复错误。此外,您还没有在任何地方捕获结果,如果您更新代码如下,它应该能正常工作:

var sections = statements.GroupBy(x => x.Section).Select(y => y.ToList()).ToList();

var result = new List<SectionCollection>();

foreach (var section in sections)
{
    SectionCollection thisSection = new SectionCollection() { Categories = new List<CategoryCollection>() };

    var categories = section.GroupBy(x => x.Category).Select(y => y.ToList()).ToList();

    foreach (var category in categories)
    {
        thisSection.Categories.Add(new CategoryCollection { Statements = category });
    }

    result.Add(thisSection);
}

但是,通过为类提供适当的构造函数和属性,并将某些逻辑移动到那里,可能会使代码更加简洁:

internal class Program
{
    static void Main(string[] args)
    {
        var statements = new List<Statement>()
        {
            new Statement(1, "a", "apple"),
            new Statement(1, "a", "banana"),
            new Statement(1, "b", "potato"),
            new Statement(2, "c", "car"),
            new Statement(2, "c", "bus"),
            new Statement(2, "d", "plane")
        };

        var sectionCollections = statements
            .GroupBy(s => s.Section)
            .Select(group => new SectionCollection(group.Key, statements))
            .ToList();
    }

    public class Statement
    {
        public Statement(int section, string category, string statementName)
        {
            Section = section;
            Category = category;
            StatementName = statementName;
        }

        public int Section { get; }

        public string Category { get; }

        public string StatementName { get; }
    }

    public class SectionCollection
    {
        public SectionCollection(int sectionName, List<Statement> statements)
        {
            SectionName = sectionName;

            Categories = statements
                .Where(s => s.Section == sectionName)
                .GroupBy(s => s.Category)
                .Select(group => new CategoryCollection(group.Key, group.ToList()))
                .ToList();
        }

        public int SectionName { get; }

        public List<CategoryCollection> Categories { get; }
    }

    public class CategoryCollection
    {
        public CategoryCollection(string categoryName, List<Statement> statements)
        {
            CategoryName = categoryName;
            Statements = statements;
        }

        public string CategoryName { get; }

        public List<Statement> Statements { get; }
    }
}

你最终会得到以下结构:

输出结构


非常感谢 @RagtimeWilly,正如我在之前的评论/回复中所说,我曾经尝试过初始化构造函数,但仍然在某些时候面临空引用的问题。最终,您在示例中设置LINQ表达式的方式指向了解决方案!我发誓每次使用Linq都会学到新东西! - Bejasc
没问题,很高兴你解决了它。 - RagtimeWilly

1
您可以使用以下代码创建一个新的SectionCollection对象:

SectionCollection thisSection = new SectionCollection();

但是你从未使用newthisSection.categories的值进行初始化,无论是在构造函数中还是明确地在外部。

因此,在内部循环中尝试访问thisSection.categories.Add时,会生成异常。


我会进一步调查,但我不确定这是否是问题。(我已经初始化了所有列表,但仍然面临上述尝试中的空引用) - Bejasc
好的@Bejasc,您可以编辑您的帖子,我会删除我的回答。 - MFisherKDX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接