what's wrong with this code (.net 2.0) - c#

I am trying to get latest date using code below, but it goes into infinte loop and displays nothing in console,
public static void sortsortyMyDates()
{
int i = 1;
DateTime[] dtList = new DateTime[20];
DateTime LatestDate = dtList[1];
dtList[1] = Convert.ToDateTime("28/05/2013 13:00:00");
dtList[2] = Convert.ToDateTime("23/04/2013 13:00:00");
dtList[3] = Convert.ToDateTime("25/03/2013 13:00:00");
dtList[4] = Convert.ToDateTime("08/04/2013 13:00:00");
while(i < dtList.Length)
{
int result = DateTime.Compare(LatestDate, dtList[i]);
if (result < 0)
continue;
else
LatestDate = dtList[i];
++i;
}
Console.WriteLine(LatestDate.ToString());
}

You have a problem with your loop logic:
if (result < 0)
continue;
If result < 0 then you don't increment i, and so the loop doesn't progress.
Also, your comparison is the wrong way around. result < 0 would mean that the currently-tested date is later than the current maximum. The reason you're getting 01/01/0001 as an output is because you're current code actually finds the earliest date, and most of your array is unintialised (and therefore a lot earlier than your test values!).
Switch your if to this instead (and remove the else entirely):
if (result < 0)
LatestDate = dtList[i];

If continue is executed, i is no longer incremented, and the loop will not terminate.

Related

C# loop going back and forth

I'd like to make a for loop that goes backwards after it reaches the end.
So when "i" reaches 10, instead of "i++" it should do "i--".
I've looked online for a little bit and couldn't find anything in C#. There's this post here, yet it's in JavaScript. I've rarely coded in JavaScript before, so I'm not sure how to translate that code into C# (if that even is possible).
I've successfully achieved this using the code below, yet it's lacking in the "readability" department. It also requires an extra variable, which I don't think is the most elegant solution.
Here's the code that I've written:
static void Main(string[] args)
{
bool reverse = false;
for(int i = 0; i <= 10;)
{
Console.WriteLine(i);
if(i == 10) reverse = true;
if(i == 0) reverse = false;
if(reverse == false) i++;
if(reverse == true) i--;
// Slow down the output
Thread.Sleep(20);
}
}
Again, this code works fine, yet I'm looking for a better solution than this mess.
Thanks in advance!
As small readability improvement I would suggest to change from the boolean flag to an integer increment value:
int increment = 1;
for (int i = 0; i >= 0 && i <= 10; i += increment)
{
Console.WriteLine(i);
if (i == 10) increment = -1;
else if (i == 0) increment = 1;
}

How to check if my date range is totally covered by CONSECUTIVE rows?

I need to check if a date range is totally covered by this date range table sorted in ascending order of dFrom, both are Date type:
dFrom dTo
----- -----
10/01 10/03
10/05 10/08
10/08 10/09
10/09 10/12
10/13 10/18
10/15 10/17
10/19 10/24
range A: 10/01-10/14 is NOT totally covered because 10/04 is missing from table.
range B: 10/10-10/20 is totally covered.
What I can think of is for a given date range like A and B, to check if each day is covered in the table:
var dRangeFrom = rangeFrom.Date; // use "var" as C# has no date type
var dRangeTo = rangeTo.Date;
int DaysCovered = 0;
int HowManyDays = (dRangeTo - dRangeFrom).TotalDays()+1;
int StartFromRow = 0;
while (dRangeFrom <= dRangeTo)
{
for (int i=StartFromRow; i<table.rows.count; i++)
{
if (table.rows[i]["dFrom"] > dRangeFrom) // optimization 1: no need to continue.
break;
if (dRangeFrom >= table.rows[i]["dFrom"] && dRangeFrom <= table.rows[i]["dTo"])
{
DaysCovered++;
StartFromRow = i; // optimization 2: next day comparison simply starts from here
break;
}
}
dRangeFrom.AddDays(1);
}
if (DaysCovered == HowManyDays)
Console.Write("Totally covered");
else
Console.Write("NOT");
One way to solve it would be to write a helper method that gets all the days in a range:
public static List<DateTime> GetDaysCovered(DateTime from, DateTime to)
{
var result = new List<DateTime>();
for (var i = 0; i < (to.Date - from.Date).TotalDays; i++)
{
result.Add(from.Date.AddDays(i));
}
return result;
}
And then we can join all the ranges from the table together and see if they match the days in the range we're trying to cover:
foreach (DataRow row in table.Rows)
{
tableDates.AddRange(GetDaysCovered(
row.Field<DateTime>("dFrom").Date,
row.Field<DateTime>("dTo").Date));
}
var rangeDates = GetDaysCovered(dRangeFrom, dRangeTo);
var missingDates = rangeDates
.Where(rangeDate => !tableDates.Contains(rangeDate))
.ToList();
if (missingDates.Any())
{
Console.Write("These dates are not covered: ");
Console.Write(string.Join(",",
missingDates.Select(date => date.ToShortDateString())));
}
else
{
Console.Write("Totally covered");
}
A naive solution is to check for each date in the range whether it is covered by any row.
var totallyCovered = true;
for (var date = rangeFrom.Date; date <= rangeTo.Date; date = date.AddDays(1))
{
var covered = dates.Any(x => date >= x.dFrom && date <= x.dTo);
if (!covered)
{
totallyCovered = false;
break;
}
}
if (totallyCovered)
{
Console.WriteLine("Totally covered.");
}
else
{
Console.WriteLine("No.");
}
That's kinda long and ugly, but thankfully you can fit that into a single LINQ query:
var dateRange = Enumerable.Range(0, 1 + rangeTo.Subtract(rangeFrom).Days)
.Select(offset => rangeFrom.Date.AddDays(offset));
var totallyCovered = dateRange.All(d => dates.Any(x => d >= x.dFrom && d <= x.dTo));
Note: This has time complexity of O(|range| * |rows|), which might be too much. To fix that you'd have to employ a more sophisticated data structure that would allow you to query ranges in logarithmic time, but since your original sample also contained nested loops, I'll assume it's unnecessary.

Cleaner way of filtering datetime by week in C#

I have a large database of records with dates. When the user selects MTD the programs counts the number of records in each 7 day span starting today (not using the weeks of the current month so I can't use .Day or .GetWeekOfMonth). Right now it looks like this:
if (options.timeRange == "MTD")
{
if (x.close_dt.Value.Date < DateTime.Today.AddMonths(-1))
{
complete = true;
}
if (DateTime.Today.AddMonths(-1).Date <= x.close_dt.Value.Date && x.close_dt.Value.Date <= DateTime.Today)
{
if (DateTime.Today.AddDays(-6).Date <= x.close_dt.Value.Date && x.close_dt.Value.Date <= DateTime.Today.Date)
{
chartObj.weeks[0]++;
}
else if (DateTime.Today.AddDays(-13).Date <= x.close_dt.Value.Date && x.close_dt.Value.Date <= DateTime.Today.AddDays(-7).Date)
{
chartObj.weeks[1]++;
}
else if (DateTime.Today.AddDays(-20).Date <= x.close_dt.Value.Date && x.close_dt.Value.Date <= DateTime.Today.AddDays(-14).Date)
{
chartObj.weeks[2]++;
}
else if (DateTime.Today.AddDays(-27).Date <= x.close_dt.Value.Date && x.close_dt.Value.Date <= DateTime.Today.AddDays(-21).Date)
{
chartObj.weeks[3]++;
}
}
}
Thank you for your help
I'm still not sure I follow you correctly, but if you just want to check for the previous 4 weeks of todays date and increment each "week" count you can simplify your code a lot.
First, keep your code DRY by not repeating common code:
private static bool DateInRange(DateTime date, DateTime minDate, DateTime maxDate)
{
return date.Date > minDate.Date && date.Date <= maxDate.Date;
}
Then change your foreach loop to this:
//Store todays date in a variable so you don't grab it every time
//(it changes every "tick" you know)
var todaysDate = DateTime.Now;
foreach(var date in sampleDates)
{
//Date is out of our 4 "week" range, skip to next loop
if(date.Date < todaysDate.AddDays(-28).Date || date.Date > todaysDate.Date)
{
continue;
}
//Using simple math we skip a lot of unnecessary code
//This loop runs 4 times
//-7 - 0
//-14 - -7
//-21 - -14
//-28 - -21
for(int i = 0; i < 4; i++)
{
if(DateInRange(date, todaysDate.AddDays((i + 1) * -7), todaysDate.AddDays(-7 * i)))
{
//Increment our "week's" counter
weekCount[i]++;
}
}
}
Fiddle here
You can do this pretty easily via Enumerable.Range and ToDictionary:
var recordsByWeek = Enumerable.Range(0, 4).ToDictionary(i => i + 1, i =>
{
var start = DateTime.Today.AddDays(7 * i);
var end = start.AddDays(7);
return records.Where(r => start <= r.Date && r.Date < end).ToList();
});
Then, you can simply do something like:
Week 1 Record Count: #recordsByWeek[1].Count;
UPDATE
I initially missed the part about it being previous weeks. The code is mostly the same, but with slight modifications. I'm leaving the original for comparison and in case someone on the interwebs stumbles upon this and actually needs that instead.
var recordsByWeek = Enumerable.Range(0, 4).Reverse().ToDictionary(i => 4 - i, i =>
{
var end = DateTime.Today.AddDays(-7 * i);
var start = end.AddDays(-7);
return records.Where(r => start <= r.Date && r.Date < end).ToList();
});
Essentially, you just reverse the enumerable so it's 3, 2, 1, 0 instead of 0, 1, 2, 3. Then, to get the week number, your subtract i from 4, which then gives you 1, 2, 3, 4. Finally, the calculation for start and end is basically flipped, so that we determine end first, using the enumerable value and then figure out start by subtracting 7 days from that.
Oh, and one more thing, this is actually exclusive of today. If you need to include today as well, such that the 4th week would count records from today, then you just need to add an extra day in when calculating end:
var end = DateTime.Today.AddDays(-7 * i + 1);

Permutation on array of type "Location", from GoogleMapsAPI NuGet Package

this is my very first post here on StackOverflow so please tell me if I did anything wrong, also english is not my native language, forgive me if there is any gramatical errors.
My question is how can I permutate the items of an array of type "Location", I need to get all possible permutations of waypoints given by the user to then calculate the best route based on time or distance. (I don't want to use the normal route calculation)
I've searched for algorithms but all of them when I put the array of type "Location[]" in the function's parameter I get the error that the object needs to be IEnumerable and I don't know how to convert to that if is even possible, I never worked with IEnumerable.
If it is of any help this is my code for calculating the route:
//Gets the waypoints from a listBox provided by the user, "mode" selects between best time and best distance
//backgroundworker so the UI dont freezes, and return the optimal waypoint order
public Location[] CalcularRota(Location[] waypoints, int mode, BackgroundWorker work, DoWorkEventArgs e)
{
//Declarations
string origem = "";
string destino = "";
Rota[] prop = new Rota[100]; //this index is the number of times the algorithm will be executed, more equals accuracy but much more time to complete
Rota bestDist = new Rota();
Rota bestTime = new Rota();
DirectionService serv = new DirectionService();
DirectionRequest reqs = new DirectionRequest();
DirectionResponse resp;
Random rnd = new Random();
Location[] rndWays;
int dist = 0;
int ti = 0;
bestDist.Distance = 1000000000; //put higher values for the first comparation to be true (end of code)
bestTime.Time = 1000000000;
if (waypoints != null)
{
reqs.Sensor = false;
reqs.Mode = TravelMode.driving;
for (int i = 0; i < prop.Length; i++) //initializes prop
prop[i] = new Rota();
for (int i = 0; i < prop.Length; i++)
{
rndWays = waypoints.OrderBy(x => rnd.Next()).ToArray(); //randomizes the order, I want to get all permutations and then test
//but I dont know how so I've been using randomized
dist = ti = 0;
origem = prop[0].ToString(); //save this particular waypoint's origin and destination
destino = prop[1].ToString();
reqs.Origin = origem;
reqs.Destination = destino;
if (waypoints.Length > 0)
reqs.Waypoints = rndWays;
resp = serv.GetResponse(reqs); //request the route with X order of waypoints to google
if (resp.Status == ServiceResponseStatus.Ok) //wait the response otherwise the program crashes
{
for (int j = 0; j < resp.Routes[0].Legs.Length; j++) //gets the distance and time of this particular order
{
ti += int.Parse(resp.Routes[0].Legs[j].Duration.Value);
dist += int.Parse(resp.Routes[0].Legs[j].Distance.Value);
}
}
prop[i].Origem = origem; //saves this waypoints order details for further comparison
prop[i].Destino = destino;
prop[i].Distance = dist;
prop[i].Time = ti;
prop[i].Order = rndWays;
work.ReportProgress(i); //report the progress
}
for (int i = 0; i < prop.Length; i++) //gets the best distance and time
{
if (bestDist.Distance > prop[i].Distance)
{
bestDist.Distance = prop[i].Distance;
bestDist.Time = prop[i].Time;
bestDist.Order = prop[i].Order;
bestDist.Origem = prop[i].Origem;
bestDist.Destino = prop[i].Destino;
}
if (bestTime.Time > prop[i].Time)
{
bestTime.Distance = prop[i].Distance;
bestTime.Time = prop[i].Time;
bestTime.Order = prop[i].Order;
bestTime.Origem = prop[i].Origem;
bestTime.Destino = prop[i].Destino;
}
}
if (bestDist.Order == bestTime.Order) //if the same waypoint order has the same time and distance
return bestDist.Order; // returns whatever bestDist.Order or bestTime.Order
else if (bestDist.Order != bestTime.Order) //if different returns corresponding to the mode selected
{
if (mode == 1) return bestDist.Order;
if (mode == 2) return bestTime.Order;
}
}
return null;
}
What I want is to permutate the waypoints given and test each permutation, I've been struggling with this for a time, if u guys could help me with any way possible would be great.
Ty.
EDIT.
I found this function here on StackOverflow:
public static bool NextPermutation<T>(T[] elements) where T : IComparable<T>
{
var count = elements.Length;
var done = true;
for (var i = count - 1; i > 0; i--)
{
var curr = elements[i];
// Check if the current element is less than the one before it
if (curr.CompareTo(elements[i - 1]) < 0)
{
continue;
}
// An element bigger than the one before it has been found,
// so this isn't the last lexicographic permutation.
done = false;
// Save the previous (bigger) element in a variable for more efficiency.
var prev = elements[i - 1];
// Have a variable to hold the index of the element to swap
// with the previous element (the to-swap element would be
// the smallest element that comes after the previous element
// and is bigger than the previous element), initializing it
// as the current index of the current item (curr).
var currIndex = i;
// Go through the array from the element after the current one to last
for (var j = i + 1; j < count; j++)
{
// Save into variable for more efficiency
var tmp = elements[j];
// Check if tmp suits the "next swap" conditions:
// Smallest, but bigger than the "prev" element
if (tmp.CompareTo(curr) < 0 && tmp.CompareTo(prev) > 0)
{
curr = tmp;
currIndex = j;
}
}
// Swap the "prev" with the new "curr" (the swap-with element)
elements[currIndex] = prev;
elements[i - 1] = curr;
// Reverse the order of the tail, in order to reset it's lexicographic order
for (var j = count - 1; j > i; j--, i++)
{
var tmp = elements[j];
elements[j] = elements[i];
elements[i] = tmp;
}
// Break since we have got the next permutation
// The reason to have all the logic inside the loop is
// to prevent the need of an extra variable indicating "i" when
// the next needed swap is found (moving "i" outside the loop is a
// bad practice, and isn't very readable, so I preferred not doing
// that as well).
break;
}
// Return whether this has been the last lexicographic permutation.
return done;
}
The usage is:
NextPermutation(array);
Doing this and putting my array (rndWays) as overload I get the following error:
The type 'Google.Maps.Location' cannot be used as type parameter 'T' in the generic type or method 'Form1.NextPermutation< T >(T[])'. There is no implicit reference conversion from 'Google.Maps.Location' to 'System.IComparable< Google.Maps.Location >'.
The problem is that Location does not implement the IComparable interface.
Change:
public static bool NextPermutation<T>(T[] elements) where T : IComparable<T>
to:
public static bool NextPermutation(Location[] elements)
And replace each CompareTo() with your own comparison function.

Aggregate value takes very long time

I have one big list of 15 min values for oround year. and I would like to aggregate them into hours. I am doing it in very simple way :
for (; from <= to; from = from.AddHours(1))
{
List<DataPoint> valuesToAgregate = data.Where(x => x.TimeStamp >= from && x.TimeStamp < from.AddHours(1)).ToList();
dailyInputData.Add(valuesToAgregate.Sum(x=>x.Val));
}
This way it takes a lot of time, like 30 seconds for 35k of values, is there any way to optimize it ? maybe use ordering functionality or some how add index to list or using grouping by instead of for loop?
Of course, if you order your list by TimeStamp previously, this will work quicker. Example:
var orderedData = data.OrderBy(item => item.TimeStamp).ToList();
int firstIndex = 0;
var from = orderedData.First().TimeStamp;
var to = orderedData.Last().TimeStamp;
while (from < to)
{
var sum = 0;
var newTo = from.AddHours(1);
while (firstIndex < data.Count && orderedData[firstIndex].TimeStamp < newTo)
{
sum += orderedData[firstIndex].Val;
++firstIndex;
}
dailyInputData.Add(sum);
from = from.AddHours(1);
}
data = data.Sort(x=>x.TimeStamp);
int counter = 0;
var boundary = from.AddHours(1);
foreach(var d in data){
if(d.TimeStamp > boundary){
boundary = boundary.AddHours(1);
counter = 0;
dailyInputData.Add(counter);
}
++counter;
}
This problem lies in the logic
the list is scanned from start to end every time to find the candidate values (your where clause)
the candidate values are inserted to another temp list
the temp list is THEN scanned from start to end to calculate the sum
The fastest approach:
sort the list
go through the items, if they belong to the current group, add the counter, otherwise you've jumped to a new group, flush the counter to record the value and start it over again

Categories

Resources