我有如下示例学生数据:
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语法根据考试日期对每个测试(数学,科学)的连续连续失败次数进行计数?
答案 0 :(得分:0)
我的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)
};
答案 1 :(得分:0)
首先(我不想在这些问题上作详细介绍),请确保在问一个问题时至少要放置结构和测试数据,以便我们可以轻松地运行和测试解决方案。花了五分钟才开始玩,甚至没有编写代码。
第二,这个问题有一个小时了,这里有几个人想提供帮助,但是您没有澄清问题中需要的简单内容。
所有这些。
这也许是您想要的,假设它是连续的组
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
要使其正常工作(它需要在内存中运行),我在这里Group results by contiguous keys借用了Microsoft的线程安全方法。这有点矫over过正,并且有更简单的方法可以实现这一点,但这是相当标志性的一段代码
扩展名
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;
}
}
}