[英]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.