subtract list first element with second one - c#

Consider that i have list dtArray1 that consist of DateTime items I mean 10.10.2010, 10.10.2015 and so on.
Could u say me how find subract of this element ?
How i can get diffeerence between 2 years?
for (Int32 index = 1; index < dtArray1.Count;index++)
{
if (dtArray1[index] >= dtArray1[index - 1])
{
dtArray1[index].Subtract(dtArray1[index - 1]);
Console.WriteLine(dtArray1[index]);
Console.ReadLine();
}
}

If your dates are of the DateTime type, you can just substract one from the other, like so:
var span = dtArray1[index] - dtArray1[index - 1];
Console.WriteLine(span.Days); // prints "1836"
The result will be of type TimeSpan. TimeSpan does not have a Years property, so you'll have to calculate the number of years yourself, keeping in mind leap years and whatnot. See How do I calculate someone's age in C#?

Related

How to appending double values to 2D Array, with access to previous index, from within foreach loop?

I have an empty 2D array called stockGains which I am trying to append with values of gains (the value of nextPrice/currentPrice) but I cannot seem to find an appropriate operator or method in C# to be able to append to my 2D array and complete my loop? Currently, I have incorrectly used .Add to give an example. The 2D array stockGains should be dynamically sized.
double[,] stockGains = {};
foreach (double currentPrice in pricesMonth1AsList){
// divide the nextPrice by currentPrice
double gains = ((currentPrice+1)/currentPrice);
// append the result to stockGains
stockGains.Add(gains);
// do this on repeat for every day of the month until the end of pricesMonth1Aslist
}


I have stored historical price data to be iterated over as a List<double> called pricesMonth1AsList which has each currentPrice stored as doubles sequentially from day 1 to day 30 (you can consider the index position of the list to be (day of the month - 1). nextPrice is the value of price the day after (which i have represented as currentPrice+1 in index position) example of the sample data is here:
20.35, 30.5, 40.2, 50 represents Day 1 [0], Day 2 [1], Day 3 [2], Day 4 [3]
 etc
For example the first gain to be calculated would be 30.5/20.35 and then this value is to be stored as the first value in index 0 of the 2D array, the second value would be 40.2/20.35, the third would be 50/20.35 also stored in index 0 of stockGains until the last day of the month. Then repeats the process again but from day 2 onwards i.e 40.2/30.5, 50/30.5 etc gains will be stored in index 1 of stockGains.
The end goal is to find the single maximum gains value in the 2D array at the end.

I am not sure if this answers your question; and I adjusted your loop to be able to access the previous element, that's why I am using for.
I believe this will be easier with a Tuple<Tx,Ty> (this has my preference)
Create a List<> out of it, and you'll have an Add:
var pricesMonth1AsList = new List<double>()
{
0,1,2,3,4,5
};
//init
var list2D = new List<Tuple<double,double>>();
for (int i = 1; i < pricesMonth1AsList.Count; i++) {
//calculate your values
//since I did not understand the calculation,
//here's just a dummy one
var currentValue = pricesMonth1AsList[i];
var previousValue = pricesMonth1AsList[i-1];
double a = previousValue/currentValue;
double b = i;
//and add it as such
list2D.Add(new Tuple<double,double>(a,b));
}
foreach(var item in list2D)
{
Console.WriteLine($"{item.Item1} - {item.Item2}");
}
Output:
0 - 1
0.5 - 2
0.6666666666666666 - 3
0.75 - 4
0.8 - 5

Find distinct TimeSpan duration from a list of TimeSpans

I'm having a bit of trouble trying to process a list of TimeSpan objects without having a lot of code which still doesn't seem to cover all eventualities, tbh, I think I've gone a bit code/logic blind now!
I have a list of TimeSpans of which overlaps will likely occur, but I need a list of TimeSpans which have no overlaps, but cover the whole duration of all TimeSpans.
For example (please note, dates are in ddMMyyyy format):
TS1: 01/01/2020 to 01/02/2020 (1 month)
TS2: 01/03/2020 to 01/05/2020 (2 months)
TS3: 01/04/2020 to 01/07/2020 (3 months with a 1 month overlap with TS2)
TS4: 01/10/2020 to 01/12/2020 (2 months)
TS5: 01/09/2020 to 01/01/2021 (4 months with a 2 month overlap with TS4)
So in this case I would expect to get 3 TimeSpans:
TSA: 01/01/2020 to 01/02/2020 (1 month - same as TS1 as there are no overlaps)
TSB: 01/03/2020 to 01/07/2020 (4 months - combination of TS2 and TS3)
TSC: 01/09/2020 to 01/01/2021 (4 months - combination of TS4 and TS5, technically only TS5 as TS4 is fully encompassed by TS5)
I've tried researching an algorithm online, but without any luck.
Any suggestions would be very welcome.
This isn't optimized at all, but semantically you can do this by adding the chunks and looking for overlaps, then merging those overlaps; something like:
using System;
using System.Collections.Generic;
using System.Globalization;
static class P
{
static void Main()
{
var results = new List<(DateTime From, DateTime To)>();
Add("01/01/2020", "01/02/2020");
Add("01/03/2020", "01/05/2020");
Add("01/04/2020", "01/07/2020");
Add("01/10/2020", "01/12/2020");
Add("01/09/2020", "01/01/2021");
// SEE BELOW, IMPORTANT
results.Sort(); // initial sort
while (MergeOneOverlap()) { }
foreach (var range in results)
{
Console.WriteLine($"{range.From:dd/MM/yyyy} - {range.To:dd/MM/yyyy}");
}
bool MergeOneOverlap()
{
for (int i = 0; i < results.Count; i++)
{
var x = results[i];
for (int j = i + 1; j < results.Count; j++)
{
var y = results[j];
if (x.Intersects(y))
{
results[i] = x.Merge(y);
results.RemoveAt(j);
results.Sort(); // retain sort while making progress
return true;
}
}
}
return false;
}
void Add(string from, string to)
=> results.Add(
(DateTime.ParseExact(from, "dd/MM/yyyy", CultureInfo.InvariantCulture),
DateTime.ParseExact(to, "dd/MM/yyyy", CultureInfo.InvariantCulture)));
}
static bool ContainsInclusive(this (DateTime From, DateTime To) range, DateTime when)
=> when >= range.From && when <= range.To;
static bool Intersects(this (DateTime From, DateTime To) x, (DateTime From, DateTime To) y)
=> x.ContainsInclusive(y.From) || x.ContainsInclusive(y.To) || y.ContainsInclusive(x.From) || y.ContainsInclusive(x.To);
static (DateTime From, DateTime To) Merge(this (DateTime From, DateTime To) x, (DateTime From, DateTime To) y)
=> (x.From < y.From ? x.From : y.From, x.To > y.To ? x.To : y.To);
}
If this is for large amounts of data, you'd have to look into being much smarter to avoid an O(N^3) problem. It may help to merge every add, if that will often keep the number of items down.
It may also be possible to reduce the complexity to O(N^2) and merging purely forwards (i.e. don't break on successful merge), but I haven't applied enough thinking to see about the implications of that. And O(N^2) is still pretty bad.
For large data, using a sorted list may help, so you can do a binary search on the start dates to find the insertion point. That is getting more complex than I care to write here, though.
I'm 95% sure that this is also fine, i.e. O(N^2):
MergeOverlaps();
foreach (var range in results)
{
Console.WriteLine($"{range.From:dd/MM/yyyy} - {range.To:dd/MM/yyyy}");
}
void MergeOverlaps()
{
results.Sort();
for (int i = 0; i < results.Count; i++)
{
var x = results[i];
for (int j = i + 1; j < results.Count; j++)
{
var y = results[j];
if (x.Intersects(y))
{
results[i] = x = x.Merge(y);
results.RemoveAt(j--);
}
}
}
}
I would suggest to try a brute force search or a depth-first search algorithm.
First you sort the timespans by starting date.
BRUTE FORCE:
You try all combinations and score them by overlap/not overlap, and you probably want to score them by how much of the total timespan is covered.
DEPTH-FIRST-SEARCH:
Write a recursive algorithm that start by adding the first interval and then add more interval and backtracks whenever an overlap occur.

Counting groups of people in age ranges

I have a little method to get the number of members in a certain age range. The range is supposed to be inclusive in both ends, i.e. if I call CountSelection(memberList, 16, 19) (where memberList is a List<Member>), I expect to get the number of members aged 16, 17, 18 and 19 summed together:
private int CountSelection(List<Member> members, int minAge, int maxAge)
{
DateTime from = DateTime.Now.AddYears(minAge * -1);
DateTime to = DateTime.Now.AddYears(maxAge * -1);
return members.Count(m =>
m.DateBorn.Date <= from.Date &&
m.DateBorn.Date >= to.Date);
}
However, my method is not reliable - sometimes it will omit members, I'm guessing when birth dates fall between ranges. In the main method, I'm calling CountSelection() several times, each with different ranges, theoretically covering all ages.
What should the query look like to guarantee that all the members will be counted?
I found out why my method was failing. I was just subtracting whole years from the from and to dates, and that resulted in ranges that looked like this (date format dd.mm.yyyy):
0-5 years - 11.04.2014-11.04.2019
6-12 years - 11.04.2007-11.04.2013
13-19 years - 11.04.2000-11.04.2006
20-26 years - 11.04.1993-11.04.1999
... and so on.
Notice the gap of almost a year between each range.
Solution:
Instead of setting the from-date like this:
DateTime from = DateTime.Now.Date.AddYears(-maxAge);
I of course have to subtract a further 1 year and add 1 day:
DateTime from = DateTime.Now.Date.AddYears(-maxAge + 1).AddDays(1);
Now the ranges look like this:
0-5 years - 12.04.2013-11.04.2019
6-12 years - 12.04.2006-11.04.2013
13-19 years - 12.04.1999-11.04.2006
20-26 years - 12.04.1992-11.04.1999
... and so on.
The final, working method looks like this:
private int CountSelection(List<Member> members, int minAge, int maxAge)
{
DateTime from = compareDate.AddYears(-maxAge+1).AddDays(1);
DateTime to = compareDate.AddYears(-minAge);
return members.Count(m =>
m.DateBorn >= from &&
m.DateBorn <= to);
}
It is easier to work with ranges with exclusive upper bounds:
private int CountSelection(List<Member> members, int minAge, int maxAgeExclusive)
{
DateTime from = compareDate.AddYears(-maxAgeExclusive);
DateTime to = compareDate.AddYears(-minAge);
return members.Count(m => m.DateBorn > from && m.DateBorn <= to);
}
Now your ranges will be mathematically more consistent.
0-6 years
6-13 years
13-20 years
20-27 years
etc
Then you can subtract one from the upper limit at the presentation layer.

Add decimal number to Date - C#

How to convert a decimal number (e.g. 2.5) to year and month (2 years and 6 months) and add it to a given date? I tried DateTime.TryParse and it didn't work.
If you are using it for years then multiply the float you have by 12. 2.5 becomes 30months. Then use the addmonths function. If I enter 5 then it will add 60 months which is 5 years
Usually you could just add a TimeSpan or use one of the Add methods, like this:
decimal yearsToAdd = (decimal)2.5;
int years = (int)Math.Floor(yearsToAdd);
decimal months = yearsToAdd - years;
int actualMonths = (int) Math.Floor(months * 12); // or Ceiling or Round
DateTime x = DateTime.Now.AddYears(years).AddMonths(actualMonths);
The problem is, that when you decimal doesn't yield an exacat number of months, how would you know how long e.g. half a month is?
28.0 / 2, 29.0 / 2, 30.0 / 2 or 31.0 / 2?
Would you take the length of the month you started with or one of the possible two months you end up with?
If you init date is dt than
dt = dt.AddMonths((int)(2.5*12));
decimal x =(decimal)2.5;
int nbYear = Convert.ToInt16(x);
var y = x - Math.Truncate(x);
int nbMonth =Convert.ToInt16 (y*12);
// MessageBox .Show (string.Format (" {0} years and {1} months ",nbYear ,nbMonth ));
DateTime dat=DateTime .Now ; // or given date
DateTime dat2 = dat.AddYears(nbYear).AddMonths(nbMonth);
If month is your smallest unit then the solution is, as pointed by many, to multiply number by 12. A more accurate alternative would be to use ticks.
decimal years=3.14592M; // No idea where this came from.
long ticks = (long)(356.0M * (decimal)TimeSpan.TicksPerDay * years);
DateTime futureDate=DateTime.Today.AddTicks(ticks);
Note that solution will not compensate for leap years. It is not difficult to extend it - you need to calculate number of leap years in the period and use average instead of 356.0M to calculate ticks per year (i.e. avg. number of days per year * ticks per day).

Secondary axis labels not working?

Okay so I have a datetime x-axis on an MSChart. I want to plot months below the first of each month and years below the change of a year. Here's what I have so far:
for (int i = 0; i < rdate.Length -1 ; i++)
{
if (rdate[i].Day == 01 && set == 0)
chart1.ChartAreas[0].AxisX.CustomLabels.Add(
rdate[i].AddDays(-20).ToOADate(), rdate[i].AddDays(20).ToOADate(),
Convert.ToString(rdate[i].ToString("MMMM")), 1, LabelMarkStyle.None);
set = 1;
if (rdate[i].Day > 01)
set = 0;
i++;
if (rdate[i].Year > rdate[i-1].Year)
chart1.ChartAreas[0].AxisX.CustomLabels.Add(
rdate[i].AddDays(-20).ToOADate(), rdate[i].AddDays(20).ToOADate(),
Convert.ToString(rdate[i].ToString("yyyy")), 2, LabelMarkStyle.None);
}
However for some reason this skips some months... The years do not show up at all.
rdate is a datetime array used to populate the x axis.
Here is an example of what my code does:
As you can see, the labels are behaving unexpectedly. I would also like to show a larger tick mark for these dates, and reduce the number of day labels based upon the date range, but I'm at a loss. Anyone done this sort of thing before?
I recently had a similar issue with MSChart when adding too many labels to the x-axis. The solution was reduce the number of ticks without losing data.
This approach worked for me but you will have to adapt it to your specific needs.
dataSeries.XValueType = ChartValueType.Auto;
dataSeries.Points.AddXY(record.DateTime, value);
I then determined the min and max dates for the given data to determine the preferred interval, your implementation will vary:
var totalDays = (maxDate.Value - minDate.Value).TotalDays;
if (totalDays < 60)
chartArea.AxisX.IntervalType = DateTimeIntervalType.Days;
else if (totalDays < 120)
chartArea.AxisX.IntervalType = DateTimeIntervalType.Weeks;
else
chartArea.AxisX.IntervalType = DateTimeIntervalType.Months;
Specify the AxisX label format:
In your case you might have to change the Format together with the interval.
chartArea.AxisX.LabelStyle.Format = Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern;
Hopefully there are some key parts that will provide value for you but you still have to modify it for your particular needs.

Categories