In C# is there a function that correlates sequential values on a IEnumerable
I have a IEnumerable. I have a custom In开发者_如何学运维terval class which just has two DateTimes inside it. I want to convert the IEnumerable to IEnumerable where n DateTimes would enumerate to n-1 Intervals.
So if I had 1st Jan, 1st Feb and 1st Mar as the DateTime then I want two intervals out, 1st Jan/1st Feb and 1st Feb/1st March.
Is there an existing C# Linq function that does this. Something like the below Correlate...
IEnumerable<Interval> intervals = dttms.Correlate<DateTime, Interval>((dttm1, dttm2) => new Interval(dttm1, dttm2));
If not I'll just roll my own.
public static IEnumerable<Timespan> Intervals(this IEnumerable<DateTime> source)
{
DateTime last;
bool firstFlag = true;
foreach( DateTime current in source)
{
if (firstFlag)
{
last = current;
firstFlag = false;
continue;
}
yield return current - last;
last = current;
}
}
or
public class Interval {DateTime Start; DateTime End;}
public static IEnumerable<Interval> Intervals(this IEnumerable<DateTime> source)
{
DateTime last;
bool firstFlag = true;
foreach( DateTime current in source)
{
if (firstFlag)
{
last = current;
firstFlag = false;
continue;
}
yield return new Interval {Start = last, End = current};
last = current;
}
}
or very generic:
public static IEnumerable<U> Correlate<T,U>(this IEnumerable<T> source, Func<T,T,U> correlate)
{
T last;
bool firstFlag = true;
foreach(T current in source)
{
if (firstFlag)
{
last = current;
firstFlag = false;
continue;
}
yield return correlate(last, current);
last = current;
}
}
var MyDateTimes = GetDateTimes();
var MyIntervals = MyDateTimes.Correlate((d1, d2) => new Interval {Start = d1, End = d2});
You could also just use Aggregate, Joel's answer would be better if you need in multiple scenarios:
var dates = new List<DateTime>
{
new DateTime(2010, 1, 1),
new DateTime(2010, 2, 1),
new DateTime(2010, 3, 1)
};
var intervals = dates.Aggregate(new List<Interval>(), (ivls, d) =>
{
if (ivls.Count != dates.Count-1)
{
ivls.Add(new Interval(d,dates[ivls.Count + 1]));
}
return ivls;
});
You can write your own extension method that will be able to do what you need.
Here's a slightly maddish solution based on LINQ:
var z = l.Aggregate(new Stack<KeyValuePair<DateTime, TimeSpan>>(),
(s, dt) =>
{
var ts = s.Count > 0 ? dt - s.Peek().Key : TimeSpan.Zero;
s.Push(new KeyValuePair<DateTime, TimeSpan>(dt, ts));
return s;
})
.Where(kv=>!kv.Value.Equals(TimeSpan.Zero))
.Select(kv => kv.Value)
.ToList();
l is an enumerable of DateTime
s.
But now that I see that you actually don't have TimeSpans but start and end times, this would look like that:
var z = l.Aggregate(new Stack<Interval>(),
(s, dt) =>
{
s.Push(s.Count > 0 ?
new Interval { Start = s.Peek().End, End = dt } : new Interval { End = dt });
return s;
})
.Where(v=> v.Start != default(DateTime))
.Reverse()
.ToList();
精彩评论