按多个字段 C# 与对象列表分组

Group by multiple fields C# with object List

我有以下数据结构:

List<List<RecordItem>> combinedList = new List<List<RecordItem>>()
{
    new List<RecordItem>()
    {
        new RecordItem() { fieldName = "StudentId", value = "S1" },
        new RecordItem() { fieldName = "Maths", value = "90" },
        new RecordItem() { fieldName = "Term", value = "1" },
    },
    new List<RecordItem>()
    {
        new RecordItem() { fieldName = "StudentId", value = "S1" },
        new RecordItem() { fieldName = "Science", value = "70" },
        new RecordItem() { fieldName = "Term", value = "1" },
    }
}; 

我需要使用 StudentId 和 Term 对其进行分组,我能够按一个字段分组 -> studentId(致谢:Enigmativity)但无法按多个字段(studentId 和学期)分组。 第一个问题是如何使用LINQ取另一个字段(term)?其次,如何完成分组的代码。

List<List<RecordItem>> groupedList =
(
    from records in combinedList
    from student in records.Take(1) 
    from term   in records.Take(?) // how do I get the term which is the 4th element???
    let StudentId = student.value
    let Term = term.value
    let Subjects = records.Skip(1)
    group Subjects by new
    {
       StudentId,Term   
    into gss
    select new []
    {
        new RecordItem() { fieldName = "StudentId", value = gss.Key.StudentId },
        new RecordItem() { fieldName = "Term", value = gss.Key.Term },
    }.Concat(gss.SelectMany(x => x)).ToList()
).ToList();

我不太明白这里发生了什么以及你选择了什么,但这是句法正确的代码:

static void Main(string[] args)
        {
            List<List<RecordItem>> combinedList = new List<List<RecordItem>>()
            {
                new List<RecordItem>()
                {
                    new RecordItem() { fieldName = "StudentId", value = "S1" },
                    new RecordItem() { fieldName = "Maths", value = "90" },
                    new RecordItem() { fieldName = "Term", value = "1" },
                },
                new List<RecordItem>()
                {
                    new RecordItem() { fieldName = "StudentId", value = "S1" },
                    new RecordItem() { fieldName = "Science", value = "70" },
                    new RecordItem() { fieldName = "Term", value = "1" },
                }
            };

            List<List<RecordItem>> groupedList =
            (
                from records in combinedList
                from student in records.Take(1)
                let StudentId = student.value
                let Term = records.ToArray().ElementAt(3).value
                let Subjects = records.Skip(1)
                group Subjects by new
                {
                    StudentId,
                    Term
                }
                    into gss
                    select new[]
                {
                    new RecordItem() { fieldName = "StudentId", value = gss.Key.StudentId },
                    new RecordItem() { fieldName = "Term", value = gss.Key.Term },
                }.Concat(gss.SelectMany(x => x)).ToList()
            ).ToList();
        }

首先,你应该尝试使用比你现有的更好的数据结构。坦率地说,使用 List> 是蹩脚的,而且必须依赖项目的顺序非常糟糕。做点什么:把你的数据放在 Dictionary<string,string> 中,或者更好的是,放在你自己的自定义 class 中。像

public class MyData
{
    public string StudentId {get; set;}
    public string Maths {get; set;}
    public string Science {get; set;}
    public string Term {get; set;}
}

我不确定这是否真的是您想要的数据结构,而且我几乎可以肯定其中一些字段应该是 string 的其他内容,但您明白了。

一旦您根据自己的意愿将数据结构化,一切都会变得更容易。

现在,如果您有以前的数据结构并想将它投射到您刚刚创建的更好的数据结构中,这并不难。

投影到字典列表中:

var dictionaries = groupedList.Select(l => 
    l.ToDictionary(
        item => item.fieldName ,
        item => item.value)
    ).ToList();

现在您可以再次投影到更好的数据结构:

var myTypedList = dictionaries.Select(d => new MyData
    {
        StudentId = d["StudentId"],
        Maths = d.ContainsKey("Maths") ? d["Maths"] : null,
        Science = d.ContainsKey("Science") ? d["Science"] : null,
        Term = d["Term"]
    }).ToList();

现在,您可以轻松进行分组:

var myResults = myTypedList.GroupBy(val => new {val.StudentId, val.Term})
    .Select(group => new {
        StudentId = group.Key.StudentId,
        Term = group.Key.Term,
        Maths = group.First(v => v.Maths != null).Maths,
        Science = group.First(v => v.Science != null).Science
}).ToList();

请记住,当您为特定问题找到正确的数据结构后,您的大部分工作就完成了。尝试尽可能具体,这将使您的代码更易于编写和阅读。试图成为 "smart" 只处理毫无意义的 classes 的叠层列表会让你无处可去。您在建模方面所做的工作是最重要的。

这是您需要的:

List<List<RecordItem>> groupedList =
(
    from records in combinedList
    from student in records.Take(1)
    let StudentId = student.value
    from term in records.Skip(2).Take(1)
    let Term = term.value
    let Subjects = records.Skip(1).Take(1)
    group Subjects by new { StudentId, Term } into gss
    select new []
    {
        new RecordItem() { fieldName = "StudentId", value = gss.Key.StudentId },
        new RecordItem() { fieldName = "Term", value = gss.Key.Term },
    }.Concat(gss.SelectMany(x => x)).ToList()
).ToList();

虽然,这个数据结构越来越差了。