简体   繁体   English

C# 列表加入列表并从另一个列表中过滤

[英]C# List Join List And Filter From Another List

Need some help constructing proper LINQ query with join.需要一些帮助来使用连接构建正确的 LINQ 查询。

I have the following setup:我有以下设置:

public class Student
{
    public string StudentName { get; set; }
    public string StudentEmail { get; set; }
}

public class Enrolled
{
    public Student Student { get; set; }
    public string Subject { get; set; }
}

public class Dropped
{
    public Student Student { get; set; }
    public string Subject { get; set; }
}


// Populating the List.
// Setup the Student List
List<Student> lstStudents = new List<Student>();

var John = new Student()
{
    StudentEmail = "john@stanford.edu",
    StudentName = "John Wayne"
};

var Maya = new Student()
{
    StudentEmail = "maya@stanford.edu",
    StudentName = "Maya Agnes"
};

var Eric = new Student()
{
    StudentEmail = "eric@stanford.edu",
    StudentName = "Eric James"
};

var Ellen = new Student()
{
    StudentEmail = "ellen@stanford.edu",
    StudentName = "Ellen Page"
};

lstStudents.Add(John);
lstStudents.Add(Maya);
lstStudents.Add(Eric);
lstStudents.Add(Ellen);

// Setup the Enrolled List
List<Enrolled> lstEnrolled = new List<Enrolled>();

// John
var JohnMath = new Enrolled() { Student = John, Subject = "Math" };
var JohnScience = new Enrolled() { Student = John, Subject = "Science" };
var JohnEnglish = new Enrolled() { Student = John, Subject = "English" };

// Maya
var MayaMath = new Enrolled() { Student = Maya, Subject = "Math" };

// Eric
var EricMath = new Enrolled() { Student = Eric, Subject = "Math" };
var EricScience = new Enrolled() { Student = Eric, Subject = "Science" };
var EricSocial = new Enrolled() { Student = Eric, Subject = "Social" };

// Ellen
var EllenMath = new Enrolled() { Student = Ellen, Subject = "Math" };
var EllenScience = new Enrolled() { Student = Ellen, Subject = "Science" };
var EllenEnglish = new Enrolled() { Student = Ellen, Subject = "English" };
var EllenSocial = new Enrolled() { Student = Ellen, Subject = "Social" };

lstEnrolled.Add(JohnMath);
lstEnrolled.Add(JohnScience);
lstEnrolled.Add(JohnEnglish);
lstEnrolled.Add(MayaMath);
lstEnrolled.Add(EricMath);
lstEnrolled.Add(EricScience);
lstEnrolled.Add(EricSocial);
lstEnrolled.Add(EllenMath);
lstEnrolled.Add(EllenScience);
lstEnrolled.Add(EllenEnglish);
lstEnrolled.Add(EllenSocial);

// Setup the Dropped List
List<Dropped> lstDropped = new List<Dropped>();

// John dropped Math
var JohnDropMath = new Dropped() { Student = John, Subject = "Math" };

// Eric dropped Social
var EricDropSocial = new Dropped() { Student = Eric, Subject = "Social" };

// Ellen Dropped Math
var EllenDropMath = new Dropped() { Student = Ellen, Subject = "Math" };

lstDropped.Add(JohnDropMath);
lstDropped.Add(EricDropSocial);
lstDropped.Add(EllenDropMath);

What I am trying to achieve is get a list of all students, and the subjects they are enrolled in one line, like:我想要实现的是获取所有学生的列表,以及他们在一行中注册的科目,例如:

Student     Subjects
-------     ----------------------------------
John        English, Science
Maya        Math
Eric        Math, Science
Ellen       English, Science, Social   

I have constructed the following so far:到目前为止,我已经构建了以下内容:

var StudentsAndCurrentSubjects = (from st in lstStudents
                                  join en in lstEnrolled 
                                      on st.StudentName equals en.Student.StudentName
                                  select new
                                  {
                                      st.StudentName,
                                      en.Subject
                                  })
                                  .ToList();

But it is giving me the result per person per subject.但它给了我每个人每个主题的结果。

I hit a wall on how to exclude the dropped items from the list.我在如何从列表中排除丢弃的项目方面遇到了障碍。

I am thinking of traversing the dropped list, like:我正在考虑遍历已删除的列表,例如:

foreach(d in lstDropped) 
{
    // Logic to Remove it from the StudentsAndCurrentSubjects 
}

But I feel that it is inefficient (especially if I have lots of rows).但我觉得它效率低下(特别是如果我有很多行)。

I also do not know how to join the subjects in one line.我也不知道如何将主题加入一行。

Looking for some help here.在这里寻求帮助。

Thanks.谢谢。

We just need to first remove dropped subjects from enrolled list.我们只需要首先从注册列表中删除删除的主题。 then group by student.然后按学生分组。 note that we have list for each student, that it has Student and Subject as its fields, we just select the name of first student from each (as they all the same) and join the subjects together.请注意,我们有每个学生的列表,它有StudentSubject作为其字段,我们只需从每个学生中选择第一个学生的姓名(因为它们都相同)并将主题连接在一起。

var result = lstEnrolled.Where(e => !lstDropped.Any(d => d.Student == e.Student && d.Subject == e.Subject))  //omit dropped courses
     .GroupBy(x => x.Student) // group results by students
     .Select(x => new {
            Name = x.First().Student.StudentName.Split(' ').First(), 
            Subjects = string.Join(", ", x.Select(e => e.Subject)) 
       }).ToArray();

for printing:用于打印:

foreach(var x in result)
Console.WriteLine($"{x.Name}\t{x.Subjects}");

LIVE DEMO现场演示

You are almost there.你快到了。 The join is correct, you just need to actually apply the grouping you want:加入是正确的,你只需要实际应用你想要的分组:

var StudentsAndCurrentSubjects = (from st in lstStudents
                                  join en in lstEnrolled 
                                      on st.StudentName equals en.Student.StudentName
                                  select new
                                  {
                                      st.StudentName,
                                      en.Subject
                                  })
                                  .GroupBy(s => s.StudentName, d => d.Subject)
                                  .Select(grp => new { StudentName = grp.Key, Subjects = grp.ToList() })
                                  .ToList();

Then you can use the projection above to display it however you want.然后你可以使用上面的投影来显示它。 Something like this maybe:可能是这样的:

foreach (var grouping in StudentsAndCurrentSubjects) {
    var studentName = grouping.StudentName;
    var subjects = string.Join(", ", grouping.Subjects);
    Console.WriteLine($"{studentName}\t{subjects}");
}

Try a GroupJoin by using the into keyword.使用into关键字尝试GroupJoin

var studentsAndCurrentSubjects = (
  from student in lstStudents
  join enrollment in lstEnrolled
    on student equals enrollment.Student
    into enrollments
  join drop in lstDrop
    on student equals drop.Student
    into drops
  let classes = enrollments.Select(e => e.Subject).Except(drops.Select(d => d.Subject))
  select new
  {
    Student = student.StudentName,
    Subjects = string.Join(", ", classes)
  })
  .ToList();

Or, you can subquery with let .或者,您可以使用let进行子查询。 This will have noticeably slower performance against larger lists than the other solution, but it should be fine in the database.与其他解决方案相比,这对于较大列表的性能明显较慢,但在数据库中应该没问题。

var studentsAndCurrentSubjects = (
  from student in lstStudents
  let enrollments = lstEnrolled.Where(x => student == x.Student).Select(x => x.Subject)
  let drops = lstDrops.Where(x => student == drop.Student).Select(x => x.Subject)
  let classes = enrollments.Except(drops)
  select new
  {
    Student = student.StudentName,
    Subjects = string.Join(", ", classes)
  })
  .ToList();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM