I have an object (a trigger) that contains a method called GetNextFireTime(DateTimeOffset after)
, which returns the next time the trigger will fire after the given input. My goal is, given an input of a list of times of unknown size n, as well as a start time, to ensure the list of times matches the first n times the trigger will fire after the start time. This is fairly easily accomplished like this
void Test(ITrigger trigger, DateTimeOffset start, DateTimeOffset[] expectedFireTimes){
var nextFire = startTime;
foreach (var expectedFire in expectedFireTimes){
nextFire = trigger.GetFireTimeAfter(nextFire);
Assert.That(nextFire, Is.EqualTo(expectedFire));
}
}
However, I then decided I would rather be able to view test results as a single assertion, rather than asserting elements one by one, so that if the first 2 fire times are correct but the 3rd is wrong, the test results show clearly what was expected and what was found. This can be done by creating a list rather than asserting in the loop:
void Test(ITrigger trigger, DateTimeOffset start, DateTimeOffset[] expectedFireTimes){
var nextFire = startTime;
var upcomingFireTimes = new List<DateTimeOffset>();
foreach (var expectedFire in expectedFireTimes){
nextFire = trigger.GetFireTimeAfter(nextFire);
upcomingFireTimes.Add(nextFire);
}
Assert.That(upcomingFireTimes, Is.EqualTo(expectedFireTimes));
}
This also works, but it made me start thinking about whether this can be accomplished via Linq. Does Linq have a method to create a list where each element is calculated based on an initial seed and the previous element? The closest I can come up with is Aggregate()
, which takes a seed and a function, but returns only a single value and I would have to store the individual elements in the list inside the function, which makes it very similar to the foreach
loop above:
var upcomingFireTimes = new List<DateTimeOffset>();
expectedFireTimes.Aggregate(
startTime,
(seed, _) => {
var nextfire = trigger.GetFireTimeAfter(seed);
upcomingFireTimes.Add(nextFire);
return nextFire;
});
My goal is a single line Linq method that, rather than ultimately returning only the last value calculated, as Aggregate()
does, returns all values calculated along the way, like this:
var upcomingFireTimes = expectedFireTimes.SomeLinqMethod(
startTime,
(seed, _) => trigger.GetFireTimeAfter(seed));
Your Linq method is Select!
DateTime firetime = startTime;
var upcomingFireTimes = expectedFireTimes.Select(s => { return fireTime = trigger.GetFireTimeAfter(fireTime) } );
You can generate whatever you need using/not using the source collection.