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. Similarly, there is 2 consecutive Math fail tests or 1 Math sequential fail.
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?
My 10 cents:
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.
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 ");
}
}
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 . Its a little overkill and there are easier ways to achieve this, however this is a fairly iconic piece of code
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;
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.