简体   繁体   English

LINQ lambda用于计数序列数

[英]LINQ lambda for counting number of sequences

I have the sample student data as follows: 我有以下示例学生数据:

ExamDate           Test           Result
01/21/2017         Math           Pass 
06/02/2017         Science        Pass
05/31/2018         Math           Fail
06/28/2018         Science        Pass 
07/03/2018         Math           Pass 
07/19/2018         Science        Fail *
08/01/2018         Math           Fail 
09/13/2018         Science        Fail *
09/15/2018         Math           Fail 
10/01/2018         Science        Fail *
12/15/2019         Math           Pass 
10/11/2019         Science        Fail *
...

In the above sorting ExamDate, there is 4 consecutive Science fail tests mark by * or (4-1) = 3 Science sequential fails in a row. 在上面的排序ExamDate中,有4个连续的科学失败测试标记为*或(4-1)= 3,科学连续失败。 Similarly, there is 2 consecutive Math fail tests or 1 Math sequential fail. 同样,有2个连续的数学失败测试或1个数学连续失败。

How can I group above data using LINQ lambda into a format like below:
    Science: 4 consecutive fail tests or (4-1) = 3 sequential fails
    Math: 2 consecutive fail tests or (2-1) = 1 sequential fails

Need help LINQ syntax to count how many sequential consecutive fails each test (Math, Science) based on sorting exam date? 需要帮助LINQ语法来根据排序考试日期计算每次测试(数学,科学)有多少连续连续失败?

My 10 cents: 我的10美分:

Suppose this is your class: 假设这是您的课程:

        public class Student
        {
            public DateTime ExamDate { get; set; }
            public string Test { get; set; }

            public bool Result { get; set; }

            public bool IsStarred { get; set; }
        }

and this is your data: 这是您的数据:

        List<Student> students = new List<Student>
        {
            new Student { ExamDate = new DateTime(2017, 01, 21), Test = "Math", Result = true, IsStarred = false },
            new Student { ExamDate = new DateTime(2017, 06, 02), Test = "Science", Result = true, IsStarred = false },
            new Student { ExamDate = new DateTime(2018, 05, 31), Test = "Math", Result = false, IsStarred = false },
            new Student { ExamDate = new DateTime(2018, 06, 28), Test = "Science", Result = true, IsStarred = false },
            new Student { ExamDate = new DateTime(2018, 07, 03), Test = "Math", Result = true, IsStarred = false },
            new Student { ExamDate = new DateTime(2018, 07, 19), Test = "Science", Result = false, IsStarred = true },
            new Student { ExamDate = new DateTime(2018, 08, 01), Test = "Math", Result = true, IsStarred = false },
            new Student { ExamDate = new DateTime(2018, 09, 13), Test = "Science", Result = false, IsStarred = true },
            new Student { ExamDate = new DateTime(2018, 09, 15), Test = "Math", Result = false, IsStarred = false },
            new Student { ExamDate = new DateTime(2018, 10, 01), Test = "Science", Result = false, IsStarred = true },
            new Student { ExamDate = new DateTime(2019, 12, 15), Test = "Math", Result = true, IsStarred = false },
            new Student { ExamDate = new DateTime(2019, 11, 10), Test = "Science", Result = false, IsStarred = true }
        };

Then you can do this: 然后,您可以执行以下操作:

  var query = from student in students
            orderby student.ExamDate
            group student.Result by student.Test into g
            select new
            {
                Group = g.Key,
                Elements = g.OrderByDescending(p2 => p2)
            };

Firstly (and i don't want to dwell on these points), please make sure when you ask a question that you at least put the structures and the test data so we can easily run and test a solution. 首先(我不想在这些方面进行详细介绍),请确保在问一个问题时至少要放置结构和测试数据,以便我们可以轻松地运行和测试解决方案。 it took 5 minutes just playing around with it before i could even write code. 我花了5分钟时间才开始编写代码。

Secondly, This question is an hour old, there are several people here who wanted to help however you gave no clarification about simple things that were needed in the question.. 其次,这个问题有一个小时了,这里有几个人想提供帮助,但是您没有澄清问题中所需的简单内容。

All that aside. 除了这些。

This maybe what you are looking for, the assumption is it counts consecutive groups 这可能是您要寻找的,假设它是连续的组

var query = list.GroupBy(x => x.Test)
                .Select(x => new
                    {
                       x.Key,
                       Results = x.ChunkBy(p => p.Result)
                                  .Select(y => new { y.Key, Count = y.Count() })
                                  .Where(z => z.Count > 1)
                    });
foreach (var item in query)
{
   Console.WriteLine($"Group key = {item.Key}");
   foreach (var inner in item.Results.Where(x => x.Key =="Fail"))
   {
      Console.WriteLine($" - {inner.Count} consecutive fail tests or ({inner.Count}-1) = {inner.Count-1} sequential fails ");
   }
}

Full Demo Here 完整的演示在这里

Example Output 示例输出

Note : this is with a more complicated data set 注意:这是一个更复杂的数据集

Group key = Math
 - 2 consecutive fail tests or (2-1) = 1  sequential fails 
 - 2 consecutive fail tests or (2-1) = 1  sequential fails 
 - 2 consecutive fail tests or (2-1) = 1  sequential fails 
Group key = Science
 - 4 consecutive fail tests or (4-1) = 3  sequential fails 
 - 8 consecutive fail tests or (8-1) = 7  sequential fails 
 - 2 consecutive fail tests or (2-1) = 1  sequential fails

For this to work (and it needs to run in memory), i have borrowed a thread safe method from Microsoft here Group results by contiguous keys . 为了使它起作用(它需要在内存中运行),我从Microsoft那里借了一个线程安全的方法,在此按连续键对结果进行分组 Its a little overkill and there are easier ways to achieve this, however this is a fairly iconic piece of code 这有点矫kill过正,并且有更简单的方法可以实现此目的,但是这是一段相当标志性的代码

Extensions 扩展

public static class MyExtensions
{
   public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
      => source.ChunkBy(keySelector, EqualityComparer<TKey>.Default);


   public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
   {
      const bool noMoreSourceElements = true;
      var enumerator = source.GetEnumerator();

      if (!enumerator.MoveNext())
         yield break;

      while (true)
      {
         var key = keySelector(enumerator.Current);    
         var current = new Chunk<TKey, TSource>(key, enumerator, value => comparer.Equals(key, keySelector(value)));

         yield return current;

         if (current.CopyAllChunkElements() == noMoreSourceElements)
            yield break;
      }
   }
}

Helper Class 助手类

public class Chunk<TKey, TSource> : IGrouping<TKey, TSource>
{

   private readonly ChunkItem _head;    
   private readonly object _mLock = new object(); 
   private IEnumerator<TSource> _enumerator;    
   private bool _isLastSourceElement;    
   private Func<TSource, bool> _predicate;    
   private ChunkItem _tail;

   public Chunk(TKey key, IEnumerator<TSource> enumerator, Func<TSource, bool> predicate)
   {
      Key = key;
      _enumerator = enumerator;
      _predicate = predicate;
      _head = new ChunkItem(enumerator.Current);
      _tail = _head;
   }

   private bool DoneCopyingChunk => _tail == null;

   public TKey Key { get; }

   public IEnumerator<TSource> GetEnumerator()
   {  
      var current = _head;

      while (current != null)
      {
         yield return current.Value;

         lock (_mLock)
            if (current == _tail)
               CopyNextChunkElement();

         current = current.Next;
      }
   }

   IEnumerator IEnumerable.GetEnumerator()
      => GetEnumerator();


   private void CopyNextChunkElement()
   {

      _isLastSourceElement = !_enumerator.MoveNext();

      if (_isLastSourceElement || !_predicate(_enumerator.Current))
      {
         _enumerator = null;
         _predicate = null;
      }
      else
         _tail.Next = new ChunkItem(_enumerator.Current);

      _tail = _tail.Next;
   }

   internal bool CopyAllChunkElements()
   {
      while (true)
         lock (_mLock)
         {
            if (DoneCopyingChunk)
               return _isLastSourceElement;

            CopyNextChunkElement();
         }
   }
   private class ChunkItem
   {
      public readonly TSource Value;
      public ChunkItem Next;

      public ChunkItem(TSource value)
      {
         Value = value;
      }
   }
}

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

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