Lambda最少发生的元素

时间:2011-12-01 20:10:16

标签: c# lambda

我有一个看起来像这样的课程:

public class TaughtSubject
{
    public int TaughtSubjectId { get; set; }

    public int TeacherId { get; set; }
    public Teacher Teacher { get; set; }

    public int FormId { get; set; }
    public Form FormId { get; set; }

    public int SubjectId { get; set; }
    public Subject Subject { get; set; }
}

我收到了这些对象的列表,我想通过SubjectId然后FormId获得最少的对象。我还想取回第一个整个元素。不只是财产。

这是我的尝试:

var teacher = taughtSubjects.GroupBy(t => t.SubjectId).OrderBy(g => g.Count()).Select(g => g.Key).First();

但这只返回SubjectId。我想要整个教师对象,并添加第二个排序条件FormId

5 个答案:

答案 0 :(得分:2)

你很亲密。您需要查找的第一项(您拥有GroupBy的结果),然后在您希望按FormId分组的查找元素的值中,并返回其中最少发生的那些教师的值

var teachers = taughtSubjects
   .GroupBy(ts => ts.SubjectId)
   .OrderBy(g => g.Count())
   .ThenBy(g=>g.Key) //tiebreaker
   .First() //gets the IEnumerable of Teachers that teach this least-taught subject
   .GroupBy(t=>t.FormId)
   .OrderBy(g=>g.Count())
   .ThenBy(g=>g.Key) //as a tiebreaker
   .First() //gets the teacher(s) with the lowest specified FormId.

这将返回一个IEnumerable的教师,其中至少有一个元素,代表教授使用最少的教学形式最少的教师。

了解系统中可能存在根本没有教过的主题。他们不会在这里代表他们,因为没有老师教会这门课。查询只会考虑至少有一位老师。

编辑:在重新阅读您的问题后,我想我明白了发生了什么。这些“教学科目”涉及教授特定“形式”或“年级”(美国系统的1年级至12年级)的特定科目(数学,阅读,科学,艺术,音乐)。

当您设置对象时,可能会出现相同主题和成绩的重复条目,但除了TaughtSubjectID字段(可能只是从计数器或标识分配)之外,它们是无法区分的。

无论你怎么想要这样做,在TaughtSubject中包含教师也会更有意义。例如,这将允许您列出教授在该科目中教学程度最低的等级中教学程度最低的科目的教师,或者如David B所暗示的那样,教授最少教授的科目和年级的整体组合。

您想要哪一个是主观的,并且存在语义差异。音乐可能是受教育程度最低的科目(仅提供给9至12年级学生),但是有两到三名教师在每个年级教授它。另一方面,数学可以在每个年级进行教学,总体而言教师人数较多,因此TaughtSubject记录的数量高于音乐,但整个组中可能只有一名6年级的数学教师。找到教学最少的科目中教学程度最低的等级会给你9年级的音乐。找到教学程度最低的整体学科成绩组合将为您提供6年级数学。

答案 1 :(得分:1)

这是第二次分组,而不是第二种分类:

IEnumerable<TaughtSubject> firstGroup = taughtSubjects
  .GroupBy(t => t.SubjectId)
  .OrderBy(g => g.Count())
  .First()  //the taughtsubjects that are members of the smallest subject group.
  .GroupBy(t => t.FormId)
  .OrderBy(g => g.Count())
  .First(); // and of these taughtsubjects, the members of the smallest form group.

  // and the first member of those.
TaughtSubject firstElement = firstGroup.First();

以上就是你所要求的,但是大多数人都希望给定形式和主题配对的最小组。就是这样做的。

IEnumerable<TaughtSubject> firstGroup = taughtSubjects
  .GroupBy(t => new {t.FormId, t.SubjectId})
  .OrderBy(g => g.Count())
  .First();

  // and the first member of those.
TaughtSubject firstElement = firstGroup.First();

答案 2 :(得分:0)

您可以使用OrderBy和ThenBy:

public class foo
{
    public int e1 { get; set; }
    public int e2 { get; set; }
    public int e3 { get; set; }
    public int e4 { get; set; }

    public foo(int e1, int e2, int e3, int e4)
    {
        this.e1 = e1;
        this.e2 = e2;
        this.e3 = e3;
        this.e4 = e4;
    }

    public override string ToString()
    {
        return string.Format("e1 = {0}\te2 = {1}\te3 = {2}\te4 = {3}",
            e1, e2, e3, e4);
    }
}

public static void Main(string[] args)
{
    List<foo> foosList = new List<foo>();
    Random r = new Random((int)DateTime.Now.Ticks);

    for (int i = 0; i < 15; i++)
        foosList.Add(new foo(r.Next(20), r.Next(20), r.Next(10), r.Next(5)));

    List<foo> result = foosList.OrderBy(e => e.e4).ThenBy(e => e.e3).ToList();
    result.ForEach(e => Console.WriteLine(e));

    Console.ReadKey();
}

输出:

e1 = 7  e2 = 4  e3 = 2  e4 = 0
e1 = 18 e2 = 12 e3 = 4  e4 = 0
e1 = 9  e2 = 8  e3 = 6  e4 = 0
e1 = 6  e2 = 8  e3 = 6  e4 = 0
e1 = 13 e2 = 14 e3 = 8  e4 = 0
e1 = 6  e2 = 15 e3 = 1  e4 = 2
e1 = 18 e2 = 10 e3 = 1  e4 = 2
e1 = 14 e2 = 3  e3 = 8  e4 = 2
e1 = 7  e2 = 3  e3 = 3  e4 = 3
e1 = 11 e2 = 6  e3 = 4  e4 = 3
e1 = 11 e2 = 17 e3 = 1  e4 = 4
e1 = 16 e2 = 1  e3 = 2  e4 = 4
e1 = 16 e2 = 11 e3 = 7  e4 = 4
e1 = 12 e2 = 5  e3 = 8  e4 = 4
e1 = 7  e2 = 7  e3 = 9  e4 = 4

答案 3 :(得分:0)

var result = from ts in TaughtSubjects
let leastSubjId = taughtSubjects.GroupBy(t => t.SubjectId).OrderBy(g => g.Count()).Select(g => g.Key).First()
where ts.SubjectId == leastSubjId
select ts

如果您愿意,可以添加其他条件。

答案 4 :(得分:-1)

我建议您不要尝试在单个表达式中执行此操作。读者很难理解正在发生的事情。将其分为两个表达式,一个用于subjectid,一个用于formid。