簡體   English   中英

LINQ lambda用於計數序列數

[英]LINQ lambda for counting number of sequences

我有以下示例學生數據:

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 *
...

在上面的排序ExamDate中,有4個連續的科學失敗測試標記為*或(4-1)= 3,科學連續失敗。 同樣,有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

需要幫助LINQ語法來根據排序考試日期計算每次測試(數學,科學)有多少連續連續失敗?

我的10美分:

假設這是您的課程:

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

            public bool Result { get; set; }

            public bool IsStarred { get; set; }
        }

這是您的數據:

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

然后,您可以執行以下操作:

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

首先(我不想在這些方面進行詳細介紹),請確保在問一個問題時至少要放置結構和測試數據,以便我們可以輕松地運行和測試解決方案。 我花了5分鍾時間才開始編寫代碼。

其次,這個問題有一個小時了,這里有幾個人想提供幫助,但是您沒有澄清問題中所需的簡單內容。

除了這些。

這可能是您要尋找的,假設它是連續的組

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

完整的演示在這里

示例輸出

注意:這是一個更復雜的數據集

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

為了使它起作用(它需要在內存中運行),我從Microsoft那里借了一個線程安全的方法,在此按連續鍵對結果進行分組 這有點矯kill過正,並且有更簡單的方法可以實現此目的,但是這是一段相當標志性的代碼

擴展

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

助手類

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