Checking if a list of DateTime objs are coherent - c#

I've got list of DateTime objects that I need to check if they are a coherent time period.
How is this done?
There might be time gaps that I need to detect and act upon.
Edit:
From what I can see the DateTime objects are sorted.
I've got a TrackerObj class and Entry class the relevant however is only the timestamp in DateTime that each tracker holds:
public class TrackerObj
{
private DateTime timeStamp;
private string trackerId;
private int voltage;
public TrackerObj(string trackerId, DateTime timeStamp, int voltage)
{
this.trackerId = trackerId;
this.timeStamp = timeStamp;
this.voltage = voltage;
}
}
The only relevant here is the timeStamp that from data I've seen are sorted.
Edit: The list is a List each object on that list contains a DateTime timeStamp. In order to determine if the periods between the DateTime are "coherent".
My definition of coherent time:
A period of time where each timestamp are after the other, without gaps (breaks in time).
DateTime format:
mm-dd-yyyy hours:minutes:seconds
private bool arePeriodsCoherent(List<TrackerObj> list)
{
// determine if all the objects on this list are without gaps. Return true if this is true. else return false.
for(int i=0; i < list.Count; i++)
{
if(list[i].timeStamp > list[i + 1].timeStamp || list[i].timeStamp == list[i + 1].timeStamp)
{return false;}
else
{return true;}
}
}
What variations does posible timeStamps contain? Will I the above code fail to catch all scenarios?

This will find any endpoints in a coherent timeperiod:
private List<int> getTimeGapIndexEndPoints(double maxTimeGapSeconds)
{
int x = 1;
List<int> timeLapsIndexes = new List<int>();
for (int i = 0; i < trackerData[trackerId].currentList.Count(); i++)
{
if (x < trackerData[trackerId].currentList.Count())
{
DateTime t1 = trackerData[trackerId].currentList[i].TimeStamp;
DateTime t2 = trackerData[trackerId].currentList[x++].TimeStamp;
TimeSpan duration = t2.Subtract(t1);
if (duration.TotalSeconds > maxTimeGapSeconds)
{
// MessageBoxResult resultb = System.Windows.MessageBox.Show(this, "After index: "+i+" "+duration+" Duration for trackerId: " + trackerId + " exceed " + maxTimeGapSeconds);
timeLapsIndexes.Add(i);
}
}
}
return timeLapsIndexes;
//for (int j = 0; j < timeLapsIndexes.Count(); j++)
//{
// MessageBoxResult resultNumbers = System.Windows.MessageBox.Show(this, "After Index (i+1): " + timeLapsIndexes[j] + " for trackerId: " + trackerId);
//}
}
Have a great day everyone. :)

Related

how to measure execution time in specific part of View [duplicate]

This question already has answers here:
Calculate the execution time of a method
(8 answers)
Closed 5 years ago.
I want to calculate execution time in a specific part of View in MVC asp.net
for example
<p>start time is A</p>
<br />
#for (int i = 0; i < 1000000; i++)
{
if (#i % 100000 == 0)
{
<p>#i</p> <br />
}
}
<p>End time is B</p>
<p>execution time is: C</p>
how to calculate A, B, C in above code ?
What I would recommend you to do here is to create an instance of Stopwatch by Stopwatch.StartNew, execute your method or your code that you want to calculate execution time. Stop the Stopwatch where you think you method should be done with execution, and then get ElapsedMilliseconds like I will do in code below:
#{
Stopwatch stopwatch = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++)
{
if (i % 100000 == 0)
{
#Html.Raw("<p>" + i.ToString() + "</p> <br />");
}
}
stopwatch.Stop();
#Html.Raw("<p>Elapsed time is: " + stopwatch.ElapsedMilliseconds.ToString() + "</p>");
}
And please, in case you might get an Error while you are trying to execute this code, be sure that you've included System.Diagnostics;
P.S If you don't like stopwatch.ElapsedMilliseconds you might check also for stopwatch.Elapsed to display elapsed time..
As I can see after Alexander's comment that you want start time, end time etc, what I suggest you to do here is to create a custom Stopwatch by inheriting System.Diagnostics.Stopwatchclass and extending it with a couple of DateTime properties.
And I will now show you how you might do it:
public class MyCustomStopWatch : Stopwatch
{
public DateTime? StartAt { get; private set; }
public DateTime? EndAt { get; private set; }
public void Reset()
{
StartAt = null;
EndAt = null;
base.Reset();
}
public void Restart()
{
StartAt = DateTime.Now;
EndAt = null;
base.Restart();
}
//This is what is important to you right now, to get data about that when you code started, and when your code finished with executing
public void Start()
{
StartAt = DateTime.Now;
base.Start();
}
public void Stop()
{
EndAt = DateTime.Now;
base.Stop();
}
}
If you are wondering HOW COULD I USE THAT CODE?
DON'T WORRY, HERE IS AN EXAMPLE BASED ON YOUR QUESTION:
#{
MyCustomStopwatch stopwatch = MyCustomStopwatch();
for (int i = 0; i < 1000000; i++)
{
if (i % 100000 == 0)
{
#Html.Raw("<p>" + i.ToString() + "</p> <br />");
}
}
stopwatch.Stop();
#Html.Raw(String.Format("<p>Stopwatch elapsed: {0}, StartAt: {1}, EndAt: {2} </p>", stopwatch.ElapsedMilliseconds.ToString(), stopwatch.StartAt.Value.ToString(), stopwatch.EndAt.Value.ToString());
}
Whatever I hope that you catch up the point and I hope that this helped you to solve your problem
You can do it like this:
#{
DateTime StartTime = DateTime.Now;
System.Diagnostics.StopWatch Watch = new System.Diagnostics.StopWatch();
#Html.Raw("<p>start time is: " + StartTime.ToString() + "</p>");
Watch.Start();
for (int i = 0; i < 1000000; i++)
{
if (i % 100000 == 0)
{
#Html.Raw("<p>" + i.ToString() + "i</p> <br />");
}
}
Watch.Stop();
DateTime EndTime = DateTime.Now;
#Html.Raw("<p>End time is " + EndTime.ToString() + "</p>");
#Html.Raw("<p>Execution time is: " + Watch.Elapsed.ToString() + "</p>");
}
Normally, I would recommend using a StopWatch to track the elapsed time but since you want the start and end times you might as well captured two dates and display the difference to calculate the time span.

C# How to search the index in an Array to find the value

I have been noodling around in C# and I am doing well so far but I am trying to find out how to Search an index in a multi dimensional array. I have an array which is for products made in each day. This displays the products made each day for each week by asking the user for input (using an InputBox) E.g. Week 1 Monday = 10 Bears, Wednesday = 20 Bears. Week 2 Tuesday = 30, Friday = 11, etc. I know about the IndexOf but this is for finding the index of a given value. I want to search the index and find and display the value.
The code is below:
class Day
{
private string monday;
private string tuesday;
private string wednesday;
private string thursday;
private string friday;
public string Monday
{
get { return monday; }
set { monday = value; }
}
public string Tuesday
{
get { return tuesday; }
set { tuesday = value; }
}
public string Wednesday
{
get { return wednesday; }
set { wednesday = value; }
}
public string Thursday
{
get { return thursday; }
set { thursday = value; }
}
public string Friday
{
get { return friday; }
set { friday = value; }
}
}
private void btnSelect_Click(object sender, EventArgs e)
{
Day[] week = new Day[4];
//Week[0] = new Day(); Ask users for input box value
E.g.Monday = Microsoft.VisualBasic.Interaction.InputBox("Enter the amount of products made on Monday for week 1", "Product Amount"),etc
//Prints the amounts
txtOutput.Text += "The product allocation is as follows:" + "\r\n" + "\r\n";
txtOutput.Text += " Mon Tue Wed Thu Fri \r\n";
int weeknum = 0;
//Iterates through and shows the values for each day of the week.
foreach (Day days in week)
{
weeknum++;
if (days != null)
{
txtOutput.Text += "Week " + weeknum + " " + days.Monday + " " + days.Tuesday + " " + days.Wednesday + " " + days.Thursday + " " + days.Friday + "\r\n";
}
}
}
"I want to search the index and find and display the value."
Sorry I might not be answering your question, but this sounds like a perfect use-case for Dictionary.
IDictionary<string, int> database = new Dictionary<string, int>
{
{"Week1 Monday", 10},
{"Week1 Wednesday", 20}
};
var userinput = "Week1 Monday";
int numberOfBears;
database.TryGetValue(userinput, out numberOfBears);
//numberOfBears = 10
For more information, you can refer to http://www.dotnetperls.com/dictionary
You'll need to loop through the array using the Array.GetLowerBound and Array.GetUpperBound methods. The Array.IndexOf and Array.FindIndex methods don't support multidimensional arrays.
For example:
string[,,] data = new string[3,3,3];
data.SetValue("foo", 0, 1, 2 );
for (int i = data.GetLowerBound(0); i <= data.GetUpperBound(0); i++)
for (int j = data.GetLowerBound(1); j <= data.GetUpperBound(1); j++)
for (int k = data.GetLowerBound(2); k <= data.GetUpperBound(2); k++)
Console.WriteLine("{0},{1},{2}: {3}", i, j, k, data[i,j,k]);
You might also find the Array.GetLength method and Array.Rank property useful. I recommend setting up a small multidimensional array and using all these methods and properties to get an idea of how they work.
There are no built-in functions for multidimensional array like Array.Find().
You basically have two choices: create your own helper methods and implement a generic search pattern there, or generate a list of domain objects correlating to the contents of the multidimensional array. I personally have tended to choose the latter option.
If you choose to write a helper method, it could look something (very roughly) like this:
// you could easily modify this code to handle 3D arrays, etc.
public static class ArrayHelper
{
public static object FindInDimensions(this object[,] target,
object searchTerm)
{
object result = null;
var rowLowerLimit = target.GetLowerBound(0);
var rowUpperLimit = target.GetUpperBound(0);
var colLowerLimit = target.GetLowerBound(1);
var colUpperLimit = target.GetUpperBound(1);
for (int row = rowLowerLimit; row < rowUpperLimit; row++)
{
for (int col = colLowerLimit; col < colUpperLimit; col++)
{
// you could do the search here...
}
}
return result;
}
}

(Dynamic programming) How to maximize room utilization with a list of meeting?

I am trying this problem using dynamic programming
Problem:
Given a meeting room and a list of intervals (represent the meeting), for e.g.:
interval 1: 1.00-2.00
interval 2: 2.00-4.00
interval 3: 14.00-16.00
...
etc.
Question:
How to schedule the meeting to maximize the room utilization, and NO meeting should overlap with each other?
Attempted solution
Below is my initial attempt in C# (knowing it is a modified Knapsack problem with constraints). However I had difficulty in getting the result correctly.
bool ContainsOverlapped(List<Interval> list)
{
var sortedList = list.OrderBy(x => x.Start).ToList();
for (int i = 0; i < sortedList.Count; i++)
{
for (int j = i + 1; j < sortedList.Count; j++)
{
if (sortedList[i].IsOverlap(sortedList[j]))
return true;
}
}
return false;
}
public bool Optimize(List<Interval> intervals, int limit, List<Interval> itemSoFar){
if (intervals == null || intervals.Count == 0)
return true; //no more choice
if (Sum(itemSoFar) > limit) //over limit
return false;
var arrInterval = intervals.ToArray();
//try all choices
for (int i = 0; i < arrInterval.Length; i++){
List<Interval> remaining = new List<Interval>();
for (int j = i + 1; j < arrInterval.Length; j++) {
remaining.Add(arrInterval[j]);
}
var partialChoice = new List<Interval>();
partialChoice.AddRange(itemSoFar);
partialChoice.Add(arrInterval[i]);
//should not schedule overlap
if (ContainsOverlapped(partialChoice))
partialChoice.Remove(arrInterval[i]);
if (Optimize(remaining, limit, partialChoice))
return true;
else
partialChoice.Remove(arrInterval[i]); //undo
}
//try all solution
return false;
}
public class Interval
{
public bool IsOverlap(Interval other)
{
return (other.Start < this.Start && this.Start < other.End) || //other < this
(this.Start < other.Start && other.End < this.End) || // this covers other
(other.Start < this.Start && this.End < other.End) || // other covers this
(this.Start < other.Start && other.Start < this.End); //this < other
}
public override bool Equals(object obj){
var i = (Interval)obj;
return base.Equals(obj) && i.Start == this.Start && i.End == this.End;
}
public int Start { get; set; }
public int End { get; set; }
public Interval(int start, int end){
Start = start;
End = end;
}
public int Duration{
get{
return End - Start;
}
}
}
Edit 1
Room utilization = amount of time the room is occupied. Sorry for confusion.
Edit 2
for simplicity: the duration of each interval is integer, and the start/end time start at whole hour (1,2,3..24)
I'm not sure how you are relating this to a knapsack problem. To me it seems more of a vertex cover problem.
First sort the intervals as per their start times and form a graph representation in the form of adjacency matrix or list.
The vertices shall be the interval numbers. There shall be an edge between two vertices if the corresponding intervals overlap with each other. Also, each vertex shall be associated with a value equal to the interval's duration.
The problem then becomes choosing the independent vertices in such a way that the total value is maximum.
This can be done through dynamic programming. The recurrence relation for each vertex shall be as follows:
V[i] = max{ V[j] | j < i and i->j is an edge,
V[k] + value[i] | k < i and there is no edge between i and k }
Base Case V[1] = value[1]
Note:
The vertices should be numbered in increasing order of their start times. Then if there are three vertices:
i < j < k, and if there is no edge between vertex i and vertex j, then there cannot be any edge between vertex i and vertex k.
Good approach is to create class that can easily handle for you.
First I create helper class for easily storing intervals
public class FromToDateTime
{
private DateTime _start;
public DateTime Start
{
get
{
return _start;
}
set
{
_start = value;
}
}
private DateTime _end;
public DateTime End
{
get
{
return _end;
}
set
{
_end = value;
}
}
public FromToDateTime(DateTime start, DateTime end)
{
Start = start;
End = end;
}
}
And then here is class Room, where all intervals are and which has method "addInterval", which returns true, if interval is ok and was added and false, if it does not.
btw : I got a checking condition for overlapping here : Algorithm to detect overlapping periods
public class Room
{
private List<FromToDateTime> _intervals;
public List<FromToDateTime> Intervals
{
get
{
return _intervals;
}
set
{
_intervals = value;
}
}
public Room()
{
Intervals = new List<FromToDateTime>();
}
public bool addInterval(FromToDateTime newInterval)
{
foreach (FromToDateTime interval in Intervals)
{
if (newInterval.Start < interval.End && interval.Start < newInterval.End)
{
return false;
}
}
Intervals.Add(newInterval);
return true;
}
}
While the more general problem (if you have multiple number of meeting rooms) is indeed NP-Hard, and is known as the interval scheduling problem.
Optimal solution for 1-d problem with one classroom:
For the 1-d problem, choosing the (still valid) earliest deadline first solves the problem optimally.
Proof: by induction, the base clause is the void clause - the algorithm optimally solves a problem with zero meetings.
The induction hypothesis is the algorithm solves the problem optimally for any number of k tasks.
The step: Given a problem with n meetings, hose the earliest deadline, and remove all invalid meetings after choosing it. Let the chosen earliest deadline task be T.
You will get a new problem of smaller size, and by invoking the algorithm on the reminder, you will get the optimal solution for them (induction hypothesis).
Now, note that given that optimal solution, you can add at most one of the discarded tasks, since you can either add T, or another discarded task - but all of them overlaps T - otherwise they wouldn't have been discarded), thus, you can add at most one from all discarded tasks, same as the suggested algorithm.
Conclusion: For 1 meeting room, this algorithm is optimal.
QED
high level pseudo code of the solution:
findOptimal(list<tasks>):
res = [] //empty list
sort(list) //according to deadline/meeting end
while (list.IsEmpty() == false):
res = res.append(list.first())
end = list.first().endTime()
//remove all overlaps with the chosen meeting
while (list.first().startTine() < end):
list.removeFirst()
return res
Clarification: This answer assumes "Room Utilization" means maximize number of meetings placed in the room.
Thanks all, here is my solution based on this Princeton note on dynamic programming.
Algorithm:
Sort all events by end time.
For each event, find p[n] - the latest event (by end time) which does not overlap with it.
Compute the optimization values: choose the best between including/not including the event.
Optimize(n) {
opt(0) = 0;
for j = 1 to n-th {
opt(j) = max(length(j) + opt[p(j)], opt[j-1]);
}
}
The complete source-code:
namespace CommonProblems.Algorithm.DynamicProgramming {
public class Scheduler {
#region init & test
public List<Event> _events { get; set; }
public List<Event> Init() {
if (_events == null) {
_events = new List<Event>();
_events.Add(new Event(8, 11));
_events.Add(new Event(6, 10));
_events.Add(new Event(5, 9));
_events.Add(new Event(3, 8));
_events.Add(new Event(4, 7));
_events.Add(new Event(0, 6));
_events.Add(new Event(3, 5));
_events.Add(new Event(1, 4));
}
return _events;
}
public void DemoOptimize() {
this.Init();
this.DynamicOptimize(this._events);
}
#endregion
#region Dynamic Programming
public void DynamicOptimize(List<Event> events) {
events.Add(new Event(0, 0));
events = events.SortByEndTime();
int[] eventIndexes = getCompatibleEvent(events);
int[] utilization = getBestUtilization(events, eventIndexes);
List<Event> schedule = getOptimizeSchedule(events, events.Count - 1, utilization, eventIndexes);
foreach (var e in schedule) {
Console.WriteLine("Event: [{0}- {1}]", e.Start, e.End);
}
}
/*
Algo to get optimization value:
1) Sort all events by end time, give each of the an index.
2) For each event, find p[n] - the latest event (by end time) which does not overlap with it.
3) Compute the optimization values: choose the best between including/not including the event.
Optimize(n) {
opt(0) = 0;
for j = 1 to n-th {
opt(j) = max(length(j) + opt[p(j)], opt[j-1]);
}
display opt();
}
*/
int[] getBestUtilization(List<Event> sortedEvents, int[] compatibleEvents) {
int[] optimal = new int[sortedEvents.Count];
int n = optimal.Length;
optimal[0] = 0;
for (int j = 1; j < n; j++) {
var thisEvent = sortedEvents[j];
//pick between 2 choices:
optimal[j] = Math.Max(thisEvent.Duration + optimal[compatibleEvents[j]], //Include this event
optimal[j - 1]); //Not include
}
return optimal;
}
/*
Show the optimized events:
sortedEvents: events sorted by end time.
index: event index to start with.
optimal: optimal[n] = the optimized schedule at n-th event.
compatibleEvents: compatibleEvents[n] = the latest event before n-th
*/
List<Event> getOptimizeSchedule(List<Event> sortedEvents, int index, int[] optimal, int[] compatibleEvents) {
List<Event> output = new List<Event>();
if (index == 0) {
//base case: no more event
return output;
}
//it's better to choose this event
else if (sortedEvents[index].Duration + optimal[compatibleEvents[index]] >= optimal[index]) {
output.Add(sortedEvents[index]);
//recursive go back
output.AddRange(getOptimizeSchedule(sortedEvents, compatibleEvents[index], optimal, compatibleEvents));
return output;
}
//it's better NOT choose this event
else {
output.AddRange(getOptimizeSchedule(sortedEvents, index - 1, optimal, compatibleEvents));
return output;
}
}
//compatibleEvents[n] = the latest event which do not overlap with n-th.
int[] getCompatibleEvent(List<Event> sortedEvents) {
int[] compatibleEvents = new int[sortedEvents.Count];
for (int i = 0; i < sortedEvents.Count; i++) {
for (int j = 0; j <= i; j++) {
if (!sortedEvents[j].IsOverlap(sortedEvents[i])) {
compatibleEvents[i] = j;
}
}
}
return compatibleEvents;
}
#endregion
}
public class Event {
public int EventId { get; set; }
public bool IsOverlap(Event other) {
return !(this.End <= other.Start ||
this.Start >= other.End);
}
public override bool Equals(object obj) {
var i = (Event)obj;
return base.Equals(obj) && i.Start == this.Start && i.End == this.End;
}
public int Start { get; set; }
public int End { get; set; }
public Event(int start, int end) {
Start = start;
End = end;
}
public int Duration {
get {
return End - Start;
}
}
}
public static class ListExtension {
public static bool ContainsOverlapped(this List<Event> list) {
var sortedList = list.OrderBy(x => x.Start).ToList();
for (int i = 0; i < sortedList.Count; i++) {
for (int j = i + 1; j < sortedList.Count; j++) {
if (sortedList[i].IsOverlap(sortedList[j]))
return true;
}
}
return false;
}
public static List<Event> SortByEndTime(this List<Event> events) {
if (events == null) return new List<Event>();
return events.OrderBy(x => x.End).ToList();
}
}
}

C# - For vs Foreach - Huge performance difference

i was making some optimizations to an algorithm that finds the smallest number that is bigger than X, in a given array, but then a i stumbled on a strange difference. On the code bellow, the "ForeachUpper" ends in 625ms, and the "ForUpper" ends in, i believe, a few hours (insanely slower). Why so?
class Teste
{
public double Valor { get; set; }
public Teste(double d)
{
Valor = d;
}
public override string ToString()
{
return "Teste: " + Valor;
}
}
private static IEnumerable<Teste> GetTeste(double total)
{
for (int i = 1; i <= total; i++)
{
yield return new Teste(i);
}
}
static void Main(string[] args)
{
int total = 1000 * 1000*30 ;
double test = total/2+.7;
var ieTeste = GetTeste(total).ToList();
Console.WriteLine("------------");
ForeachUpper(ieTeste.Select(d=>d.Valor), test);
Console.WriteLine("------------");
ForUpper(ieTeste.Select(d => d.Valor), test);
Console.Read();
}
private static void ForUpper(IEnumerable<double> bigList, double find)
{
var start1 = DateTime.Now;
double uppper = 0;
for (int i = 0; i < bigList.Count(); i++)
{
var toMatch = bigList.ElementAt(i);
if (toMatch >= find)
{
uppper = toMatch;
break;
}
}
var end1 = (DateTime.Now - start1).TotalMilliseconds;
Console.WriteLine(end1 + " = " + uppper);
}
private static void ForeachUpper(IEnumerable<double> bigList, double find)
{
var start1 = DateTime.Now;
double upper = 0;
foreach (var toMatch in bigList)
{
if (toMatch >= find)
{
upper = toMatch;
break;
}
}
var end1 = (DateTime.Now - start1).TotalMilliseconds;
Console.WriteLine(end1 + " = " + upper);
}
Thanks
IEnumerable<T> is not indexable.
The Count() and ElementAt() extension methods that you call in every iteration of your for loop are O(n); they need to loop through the collection to find the count or the nth element.
Moral: Know thy collection types.
The reason for this difference is that your for loop will execute bigList.Count() at every iteration. This is really costly in your case, because it will execute the Select and iterate the complete result set.
Furthermore, you are using ElementAt which again executes the select and iterates it up to the index you provided.

Merging overlapping time intervals?

I have the following:
public class Interval
{
DateTime Start;
DateTime End;
}
I have a List<Interval> object containing multiple intervals. I am trying to achieve the following (I used numbers to make it easy to understand):
[(1, 5), (2, 4), (3, 6)] ---> [(1,6)]
[(1, 3), (2, 4), (5, 8)] ---> [(1, 4), (5,8)]
I currently do this in Python as follows:
def merge(times):
saved = list(times[0])
for st, en in sorted([sorted(t) for t in times]):
if st <= saved[1]:
saved[1] = max(saved[1], en)
else:
yield tuple(saved)
saved[0] = st
saved[1] = en
yield tuple(saved)
but am trying to achieve the same in C# (LINQ would be best but optional). Any suggestions on how to do this efficiently?
Here's a version using yield return - I find it easier to read than doing an Aggregate query, although it's still lazy evaluated. This assumes you've ordered the list already, if not, just add that step.
IEnumerable<Interval> MergeOverlappingIntervals(IEnumerable<Interval> intervals)
{
var accumulator = intervals.First();
intervals = intervals.Skip(1);
foreach(var interval in intervals)
{
if ( interval.Start <= accumulator.End )
{
accumulator = Combine(accumulator, interval);
}
else
{
yield return accumulator;
accumulator = interval;
}
}
yield return accumulator;
}
Interval Combine(Interval start, Interval end)
{
return new Interval
{
Start = start.Start,
End = Max(start.End, end.End),
};
}
private static DateTime Max(DateTime left, DateTime right)
{
return (left > right) ? left : right;
}
I was beset by "Not Created Here" syndrome tonight, so here's mine. Using an Enumerator directly saved me a couple lines of code, made it clearer (IMO), and handled the case with no records. I suppose it might run a smidge faster as well if you care about that...
public IEnumerable<Tuple<DateTime, DateTime>> Merge(IEnumerable<Tuple<DateTime, DateTime>> ranges)
{
DateTime extentStart, extentEnd;
using (var enumerator = ranges.OrderBy(r => r.Item1).GetEnumerator()) {
bool recordsRemain = enumerator.MoveNext();
while (recordsRemain)
{
extentStart = enumerator.Current.Item1;
extentEnd = enumerator.Current.Item2;
while ((recordsRemain = enumerator.MoveNext()) && enumerator.Current.Item1 < extentEnd)
{
if (enumerator.Current.Item2 > extentEnd)
{
extentEnd = enumerator.Current.Item2;
}
}
yield return Tuple.Create(extentStart, extentEnd);
}
}
}
In my own implementation, I use a TimeRange type to store each Tuple<DateTime, DateTime>, as other here do. I didn't include that here simply to stay focused / on-topic.
This may not be the prettiest solution, but it may work as well
public static List<Interval> Merge(List<Interval> intervals)
{
var mergedIntervals = new List<Interval>();
var orderedIntervals = intervals.OrderBy<Interval, DateTime>(x => x.Start).ToList<Interval>();
DateTime start = orderedIntervals.First().Start;
DateTime end = orderedIntervals.First().End;
Interval currentInterval;
for (int i = 1; i < orderedIntervals.Count; i++)
{
currentInterval = orderedIntervals[i];
if (currentInterval.Start < end)
{
end = currentInterval.End;
}
else
{
mergedIntervals.Add(new Interval()
{
Start = start,
End = end
});
start = currentInterval.Start;
end = currentInterval.End;
}
}
mergedIntervals.Add(new Interval()
{
Start = start,
End = end
});
return mergedIntervals;
}
Any feedback will be appreciated.
Regards
This kind of merging would typically be considered as a fold in functional languages. The LINQ equivalent is Aggregate.
IEnumerable<Interval<T>> Merge<T>(IEnumerable<Interval<T>> intervals)
where T : IComparable<T>
{
//error check parameters
var ret = new List<Interval<T>>(intervals);
int lastCount
do
{
lastCount = ret.Count;
ret = ret.Aggregate(new List<Interval<T>>(),
(agg, cur) =>
{
for (int i = 0; i < agg.Count; i++)
{
var a = agg[i];
if (a.Contains(cur.Start))
{
if (a.End.CompareTo(cur.End) <= 0)
{
agg[i] = new Interval<T>(a.Start, cur.End);
}
return agg;
}
else if (a.Contains(cur.End))
{
if (a.Start.CompareTo(cur.Start) >= 0)
{
agg[i] = new Interval<T>(cur.Start, a.End);
}
return agg;
}
}
agg.Add(cur);
return agg;
});
} while (ret.Count != lastCount);
return ret;
}
I made the Interval class generic (Interval<T> where T : IComparable<T>), added a bool Contains(T value) method, and made it immutable, but you should not need to change it much if you want to use the class definition as you have it now.
I used TimeRange as a container storing the ranges:
public class TimeRange
{
public TimeRange(DateTime s, DateTime e) { start = s; end = e; }
public DateTime start;
public DateTime end;
}
It divides the problem in combining two time ranges. Therefor, the current time range (work) is matched with the time ranges previously merged. If one of the previously added time ranges is outdated, it is dropped and the new time range (combined from work and the matching time range) is used.
The cases I figured out for two ranges () and [] are as follows:
[] ()
([])
[(])
[()]
([)]
()[]
public static IEnumerable<TimeRange> Merge(IEnumerable<TimeRange> timeRanges)
{
List<TimeRange> mergedData = new List<TimeRange>();
foreach (var work in timeRanges)
{
Debug.Assert(work.start <= work.end, "start date has to be smaller or equal to end date to be a valid TimeRange");
var tr = new TimeRange(work.start, work.end);
int idx = -1;
for (int i = 0; i < mergedData.Count; i++)
{
if (tr.start < mergedData[i].start)
{
if (tr.end < mergedData[i].start)
continue;
if (tr.end < mergedData[i].end)
tr.end = mergedData[i].end;
}
else if (tr.start < mergedData[i].end)
{
tr.start = mergedData[i].start;
if (tr.end < mergedData[i].end)
tr.end = mergedData[i].end;
}
else
continue;
idx = i;
mergedData.RemoveAt(i);
i--;
}
if (idx < 0)
idx = mergedData.Count;
mergedData.Insert(idx, tr);
}
return mergedData;
}

Categories