【问题标题】:finding sequential patterns of objects in a list with particular properties在具有特定属性的列表中查找对象的顺序模式
【发布时间】:2015-07-21 23:05:23
【问题描述】:

我有这样的课程:

public class TestResults
{
public String TestName {get;set;}
public Int32 StudentID {get;set;}
public Decimal Score {get;set;}
public Date TestTaken {get;set;}
}

所以有些对象 mike 看起来像这样:

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# 领域中的模式序列。

我听人谈论过 F#,但没有这方面的实际经验。此外,我认为我所说的“模式匹配”比我经常遇到的一些simple string-pattern-matching 涉及更多。

【问题讨论】:

    标签: c# linq pattern-matching sequence


    【解决方案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 条件,它只返回具有“大的得分跳跃”的项目。我将“分数大幅提升”定义为最高分数和最低分数之间的差异大于 0.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 子句中,我们检查具有最低分数的TestResults 是否在 60-80 范围内。我们检查具有最高分数的TestResults 是否在 90+ 范围内。最后,我们检查最低分数是否发生在最高分数出现之前的日期。

    【讨论】:

      【解决方案2】:

      你可以这样做:

      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;
                 }
             }
         }
      }
      

      这为您提供了所有范围的跳跃。如果你想缩小范围,你可以为分数 > 60 添加一个 .Where(),并相应地调整 differenceLimit

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-03
        • 1970-01-01
        • 2013-02-13
        • 1970-01-01
        • 2018-08-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多