Search code examples
c#.netdatetime.net-6.0

how to verify whether the all months are listed for a year in a given date range


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
}

Solution

  • With LINQ you can write:

    bool allMonthsAreListed = dateGenerators
        .GroupBy(dg => dg.GeneratedDate.Year)
        .All(g => g.Count() % 12 == 0);
    

    Explanation:

    1. We group by year.
    2. For each group get the month count.
    3. 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".

    1. Do only need to know that some years or sets are not complete?
    2. You you need to identify the years/sets?
    3. 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:

    1. We extract the date from the DateGenerators.
    2. We group by year.
    3. We test whether the count of months per year is divisible by 12 and ...
    4. ... that all months occur the same number of times. To do this, we:
      1. group by month
      2. get the count of each month group
      3. get the distinct number of counts for all the groups
      4. 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) });
    }