I have below class
public class DateGenerator
{
public DateOnly GeneratedDate {get; set;}
}
and list of dateGenerators List<DateGenerator> dateGenerators;
as below
I need to verify whether all months are present for a year within the given range. For example, I have given a listed range; I need to identify the years first, and then for that year, whether all months are listed or not.
Here I have the same years along with the months listed, so the above condition also applies for the same years.
For example, not all months are listed below for the year 2009
and I have duplicate data for 2011
. All months are not listed in the second set of the same data year, 2011
, so I need to identify this.
Date (Month/Day/Year) |
---|
7/1/2009 |
8/1/2009 |
9/1/2009 |
10/1/2009 |
11/1/2009 |
12/1/2009 |
1/1/2010 |
2/1/2010 |
3/1/2010 |
4/1/2010 |
5/1/2010 |
6/1/2010 |
7/1/2010 |
8/1/2010 |
9/1/2010 |
10/1/2010 |
11/1/2010 |
12/1/2010 |
1/1/2011 |
2/1/2011 |
3/1/2011 |
4/1/2011 |
5/1/2011 |
6/1/2011 |
7/1/2011 |
8/1/2011 |
9/1/2011 |
10/1/2011 |
11/1/2011 |
12/1/2011 |
4/1/2011 |
5/1/2011 |
6/1/2011 |
7/1/2011 |
8/1/2011 |
9/1/2011 |
I can group by GeneratedDate
and extract the year and compare, but I am unsure how to tackle the duplicate data. Could anyone please let me know how to approach the same to identify
Thanks in advance!!
Sample method:
public bool verifyCompleteMonthlySet(List<DateGenerator> dateGenerators)
{
//Identify whether all months are present for a year in a
//given a list, and also identify if there is
//any duplicate set found for a year, and all months are present for that same year as well
}
CodePudding user response:
With LINQ you can write:
bool allMonthsAreListed = dateGenerators
.GroupBy(dg => dg.GeneratedDate.Year)
.All(g => g.Count() % 12 == 0);
Explanation:
- We group by year.
- For each group get the month count.
- If the month count is divisible by 12 (with no remainder), all the sets in this year are complete.
Note that this is not perfect, e.g. there could be two incomplete sets in the same year whose month count adds up to 12. We would need to have a way to identify the sets. Do you have a set id or the like?
Also, I am not sure what you mean by "I need to identify this".
- Do only need to know that some years or sets are not complete?
- You you need to identify the years/sets?
- Do you need to know the missing months?
Since we do not have a way to identify sets, things get a bit more complicated:
bool allMonthsAreListed = dateGenerators
.Select(dg => dg.GeneratedDate)
.GroupBy(d => d.Year)
.All(
g => g.Count() % 12 == 0 &&
g.GroupBy(d => d.Month)
.Select(g2 => g2.Count())
.Distinct()
.Count() == 1
);
Explanation:
- We extract the date from the DateGenerator's.
- We group by year.
- We test whether the count of months per year is divisible by 12 and ...
- ... that all months occur the same number of times. To do this, we:
- group by month
- get the count of each month group
- get the distinct number of counts for all the groups
- test if this number is 1.
Note that if we have one full set, each month occurs once, if we have two full sets, each month occurs twice, etc. If the first set is complete and the second set has only 11 months, the we have 11 times a count of 2 and one time a count of 1. I.e., the distinct number of counts is 2. This identifies an incomplete set.
Tested with
List<DateGenerator> dateGenerators = new();
// Year 2010 with one complete set
for (int i = 1; i <= 12; i ) {
dateGenerators.Add(new() { GeneratedDate = new DateOnly(2010, i, 1) });
}
// Year 2011 with 12 months but from two incomplete sets
for (int i = 1; i <= 10; i ) { // Set 1: months 1 to 10
dateGenerators.Add(new() { GeneratedDate = new DateOnly(2011, i, 1) });
}
for (int i = 5; i <= 6; i ) { // Set 2: months 5 and 6
dateGenerators.Add(new() { GeneratedDate = new DateOnly(2011, i, 1) });
}
CodePudding user response:
Also using System.Linq.GroupBy
but then doing a forensic by removing from all
.
static void Main(string[] args)
{
Console.Title = "Missing months";
// Get an array of DateTime.
var dates = inputData.Trim().Split(',').Select(_=>DateTime.Parse(_)).ToArray();
foreach (var year in dates.GroupBy(_=>_.Year))
{
Console.WriteLine($"{year.Key}");
var months = year.Select(_ => _.Month).Distinct().ToArray();
if (months.Length == 12)
{
Console.WriteLine($"All months present.");
}
else
{
var all = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
foreach (var month in months)
{
all.Remove(month);
}
Console.WriteLine($"{year.Key} missing {string.Join(", ", all )}");
}
foreach (var dup in year.GroupBy(_ => _).Where(_ => _.Count() > 1))
{
Console.WriteLine($"Duplicate dates: {dup.Key.ToShortDateString()} ({dup.Count()})");
}
Console.WriteLine();
}
Console.ReadKey();
}
Where inputData
uses the data set from your post:
const string inputData = @"
7/1/2009,
8/1/2009,
9/1/2009,
10/1/2009,
11/1/2009,
12/1/2009,
1/1/2010,
2/1/2010,
3/1/2010,
4/1/2010,
5/1/2010,
6/1/2010,
7/1/2010,
8/1/2010,
9/1/2010,
10/1/2010,
11/1/2010,
12/1/2010,
1/1/2011,
2/1/2011,
3/1/2011,
4/1/2011,
5/1/2011,
6/1/2011,
7/1/2011,
8/1/2011,
9/1/2011,
10/1/2011,
11/1/2011,
12/1/2011,
4/1/2011,
5/1/2011,
6/1/2011,
7/1/2011,
8/1/2011,
9/1/2011";