我有一个这样的课程:
public class TestResults
{
public String TestName {get;set;}
public Int32 StudentID {get;set;}
public Decimal Score {get;set;}
public Date TestTaken {get;set;}
}
所以有些物品看起来像这样:
test.TestName = "Big Important Test";
test.StudentID = 17;
test.Score = 0.75M;
test.TestTaken = "1/1/2015";
tests.add(test);
test.TestName = "Big Important Test";
test.StudentID = 12;
test.Score = 0.89M;
test.TestTaken = "1/1/2015";
tests.add(test);
test.TestName = "Sneaky Pop Quiz in Chemistry";
test.StudentID = 17;
test.Score = 0.97M;
test.TestTaken = "2/1/2015";
tests.add(test);
test.TestName = "Sneaky Pop Quiz in Chemistry";
test.StudentID = 17;
test.Score = 0.97M;
test.TestTaken = "2/1/2015";
tests.add(test);
我想要确定的是“对于每个学生,向我展示他们的分数大幅跳跃的学生?” I asked a similar question a while back in the dba.stackexchange.com world并使用了LEAD函数,但现在我想将逻辑移到C#中。
因此,我想要编写的具体问题是(作为示例):
告诉我从60%和70%的范围跳到学生的学生 90范围。
我知道我可以写一个rat's nest of loops and branching logic,但想知道是否有更优雅,更全面的方法来识别LINQ / C#land中的模式序列。
我听说人们谈论F#,但没有实际经验。另外,我认为我所谈论的“模式匹配”比我持续运行的simple string-pattern-matching中的一些涉及更多。
答案 0 :(得分:1)
您可以这样做:
const decimal differenceLimit = 0.05M;
var studentIdsWithJump = tests.GroupBy (g => g.StudentID)
.Where(g => g.OrderBy(c => c.Score)
.GroupAdjacentBy((first, second) =>
first.Score + differenceLimit < second.Score
).Count() > 1
)
.Select(g => g.Key);
使用辅助方法from here:
public static class LinqExtensions
{
public static IEnumerable<IEnumerable<T>> GroupAdjacentBy<T>(this IEnumerable<T> source, Func<T, T, bool> predicate)
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
var list = new List<T> { e.Current };
var pred = e.Current;
while (e.MoveNext())
{
if (predicate(pred, e.Current))
{
list.Add(e.Current);
}
else
{
yield return list;
list = new List<T> { e.Current };
}
pred = e.Current;
}
yield return list;
}
}
}
}
这为您提供了所有范围的跳转。如果你想缩小它,你可以添加一个.Where()得分&gt; 60,并相应调整differenceLimit
答案 1 :(得分:1)
您可以使用LINQ来获得答案。以下是您可以执行此操作的示例:
var scores = tests.GroupBy(t => t.StudentID)
.Select(g => new { StudentID = g.Key, Min = g.Min(i => i.Score), Max = g.Max(i => i.Score) })
.Where(s => s.Max - s.Min > .20M);
foreach(var score in scores)
Console.WriteLine("Student: {0} Jump: {1}", score.StudentID, score.Max - score.Min);
LINQ语句首先按StudentID
分组。接下来,它将每个组的StudentID和Min和Max分数投影到新的匿名类型。最后,应用where条件,该条件仅返回“得分大跳跃”的项目。我定义“得分大跳”,因为最高得分和最低得分之间的差异大于.20。
注意:即使学生在列表中的分数超过2分,此代码也会起作用。
更新:
由于您更新了帖子,我更了解您的问题。这是一个更新的答案:
var scores = tests.GroupBy(t => t.StudentID)
.Select(g => new { StudentID = g.Key, Min = g.OrderBy(i => i.Score).First(), Max = g.OrderByDescending(i => i.Score).First() })
.Where(s => (s.Min.Score >= .60M & s.Min.Score < .80M) & s.Max.Score >= .90M & s.Min.TestTaken < s.Max.TestTaken);
foreach(var score in scores)
Console.WriteLine("Student: {0} Jump: {1}", score.StudentID, score.Max.Score - score.Min.Score);
这使用了类似的方法,但不是记录匿名类型的最小和最大分数,而是记录具有最小分数和最大分数的TestResults
实例。在where where子句中,我们检查具有最小分数的TestResults
是否在60-80范围内。我们检查具有最高分数的TestResults
是否在90+范围内。最后,我们检查最小分数是否发生在最大分数发生之前的日期。