2011-01-14 4 views
6

Я получил свой собственный способ сделать это, но я не уверен, его самое лучшее, в C#найти пропавший месяц

Учитывая в List<DateTime>, в DateTime startDate и DateTime endDate. Как бы вы вернуть новый List<DateTime> для каждого месяца между startDate и endDate, который не включены в первоначальный List<DateTime> включено в startDate и endDate.

Даты не гарантируются как начало месяца, может быть любая дата месяца.

startDate и endDate может охватывать несколько лет.

Возвращенный список должен содержать первый день каждого месяца, который отсутствует.

Спасибо, и я надеюсь, что это имеет смысл.

+0

Когда вы говорите «месяц», включает ли он год? Являются ли январь 2010 года и январь 2011 года месяцами в вашем описании? –

+0

Можете ли вы привести пример, если это возможно? – Pabuc

+0

Если вы укажете startDate как 15 января, вы должны включить январь? Поскольку вы указываете, что хотите 1-го числа этого месяца. –

ответ

3
var list = new List<DateTime> 
    { 
     new DateTime(1231223423433132), 
     new DateTime(13223123132), 
     new DateTime(12333123132), 
     new DateTime(123345123132), 
     DateTime.Now, 
     new DateTime(5634534553) 
    }; 

var allYearMonthes = list.Select(o => 
          Eumerable.Range(1, 12) 
           .Select(q => new { o.Year, Month = q })) 
          .SelectMany(o => o); 

var enumerable = allYearMonthes.Except(list.Select(o => new { o.Year, o.Month })); 

var dateTimes = enumerable.Select(o => new DateTime(o.Year, o.Month, 1)); 

EDIT: для тех, кто заинтересован в, вероятно, полное решение:

DateTime StartDate = DateTime.Now, EndDate = DateTime.Now.AddYears(5).AddMonths(2); 
var allYearMonthes = Enumerable.Range(StartDate.Year, EndDate.Year - StartDate.Year -1) 
           .Select(o => Enumerable.Range(1, 12) 
           .Select(q => new { Year = o, Month = q })) 
           .SelectMany(o => o); 

var enumerable = allYearMonthes.Except(list.Select(o => new { o.Year, o.Month })); 
var dateTimes = enumerable.Select(o => new DateTime(o.Year, o.Month, 1)); 
+0

Ваш код включал все месяцы в году. Например, если у вас есть дата начала 5/1/2010, то ваш код будет включать 1/1/2010, 2/1/2010, 3/1/2010, 4/1/2010. Такая же история для даты окончания. Я немного изменил ваш код, чтобы предоставить только даты между начальными данными и датой окончания. Я дал вам +1. –

+0

Да, я пропустил это, но это тривиальная задача, чтобы отфильтровать даты, чтобы включить только даты между датой начала и окончания, я пропустил некоторые детали вопроса, но решение можно было легко принять, и вы совершенно правы :-) –

+0

Это не будет работать, если ваш набор данных содержит пробел в течение года. Он производит только продукцию в течение многих лет, когда в этом году есть элемент. Рассмотрим случай, когда ввод пуст, или случай, когда дата начала происходит за год до первой даты в списке. –

3

Ну, предполагая, что в том же месяце в разные годы считаются разными:

private List<DateTime> GetUnincludedMonths(DateTime startDate, DateTime endDate, 
               IEnumerable<DateTime> dates) 
    { 
     var allMonths = new HashSet<Tuple<int, int>>(); //month, year 
     DateTime date = startDate; 
     while (date <= endDate) 
     { 
      allMonths.Add(Tuple.Create(date.Month, date.Year)); 
      date = date.AddMonths(1); 
     } 
     allMonths.Add(Tuple.Create(endDate.Month, endDate.Year)); 

     allMonths.ExceptWith(dates.Select(dt => Tuple.Create(dt.Month, dt.Year))); 
     return allMonths.Select(t => new DateTime(t.Item2, t.Item1, 1)).ToList(); 
    } 
+0

Это пропустит последний месяц, если endDate имеет более низкое значение дня, чем startDate, т.е. 15. января до 1. февраля вы добавляете месяц и добираетесь до 15. февраля, который находится выше endDate. –

+0

Как и ответ, но, к сожалению, я не могу использовать 4.0, поэтому у меня нет доступа к кортежу –

+0

@mjmcloug Итак, используйте «KeyValuePair» –

-1
public IList<DateTime> GetMissingMonths(IList<DateTime> currentList, DateTime startDate, DateTime endDate) 
    { 
     // Create a list for the missing months 
     IList<DateTime> missingList = new List<DateTime>(); 

     // Select a startdate 
     DateTime testingDate = startDate; 

     // Begin by the month of startDate and ends with the month of endDate 
     // month of startDate and endDate included 
     while(testingDate <= endDate) 
     { 
      if (currentList.Count(m => m.Month == testingDate.Month && m.Year == testingDate.Year) == 0) 
      { 
       missingList.Add(new DateTime(testingDate.Year, testingDate.Month, 1)); 
      } 
      testingDate = testingDate.AddMonths(1); 
     } 
     return missingList; 
    } 
+0

@Downvoter Пожалуйста, объясните, чтобы все могли узнать из ваших результатов. – hwcverwe

2

Вот что я буду делать:

static IEnumerable<DateTime> GetMissingMonths(IEnumerable<DateTime> currentDates, DateTime startDate, DateTime endDate) 
{ 
    var yearMonths = new HashSet<Tuple<int, int>>(currentDates.Select(d => Tuple.Create(d.Year, d.Month))); 
    DateTime current = new DateTime(startDate.Year, startDate.Month, 1); 
    if (current < startDate) 
     current = current.AddMonths(1); 
    while (current <= endDate) 
    { 
     if (!yearMonths.Contains(Tuple.Create(current.Year, current.Month))) 
     { 
      yield return current; 
     } 
     current = current.AddMonths(1); 
    } 
} 

EDIT: если вы не можете использовать кортежи вы можете использовать анонимный тип, а не с вспомогательным методом для создания HashSet:

static IEnumerable<DateTime> GetMissingMonths(IEnumerable<DateTime> currentDates, DateTime startDate, DateTime endDate) 
{ 
    var yearMonths = MakeHashSet(currentDates.Select(d => new { d.Year, d.Month })); 
    DateTime current = new DateTime(startDate.Year, startDate.Month, 1); 
    if (current < startDate) 
     current = current.AddMonths(1); 
    while (current <= endDate) 
    { 
     if (!yearMonths.Contains(new { current.Year, current.Month })) 
     { 
      yield return current; 
     } 
     current = current.AddMonths(1); 
    } 
} 

static HashSet<T> MakeHashSet<T>(IEnumerable<T> source) 
{ 
    return new HashSet<T>(source); 
} 

Метод MakeHashSet позволяет введите тип вывода для создания HashSet<T>, когда T является анонимным типом.

1

LINQPad -рабочая решение:

void Main() 
{ 
    var dates = new List<DateTime> 
    { 
     new DateTime(2011, 1, 1), 
     new DateTime(2011, 3, 5), 
     new DateTime(2011, 7, 28), 
    }; 
    var startDate = new DateTime(2011, 1, 1); 
    var endDate = new DateTime(2012, 12, 31); 
    var existingMonths = 
     (from dt in dates 
     select dt.Year * 12 + dt.Month - 1).Distinct().ToArray(); 
    var missingMonths = 
     from ym in Enumerable.Range(
      startDate.Year * 12 + startDate.Month - 1, 
      (endDate.Year * 12 + endDate.Month) - (startDate.Year * 12 + startDate.Month) + 1) 
     where !existingMonths.Contains(ym) 
     select new DateTime(ym/12, ym % 12 + 1, 1); 
    missingMonths.Dump(); 
} 
0
static void Main(string[] args) 
     { 
      var days = (new string[] { "3/23/2000", "7/3/2004", "1/3/2004", "3/1/2011" }) 
         .Select(a => Convert.ToDateTime(a)); 

      days = days.Select(a => a.AddDays(1 - (a.Day))).Distinct(); 
      days = days.OrderBy(a => a); 

      var missingMonths = GetMissingMonths(days).ToList(); 
     } 

     private static IEnumerable<DateTime> GetMissingMonths(IEnumerable<DateTime> days) 
     { 
      DateTime previous = days.First(); 
      foreach (var current in days.Skip(1)) 
      { 
       int months = (current.Month - previous.Month) + 
            12 * (current.Year - previous.Year); 
       for (int i = 1; i < months; i++) 
       { 
        yield return previous.AddMonths(i); 
       } 
       previous = current; 
      } 
     } 
1
public IEnumerable<DateTime> GetMissingMonths(
    DateTime startDate, 
    DateTime endDate, 
    IEnumerable<DateTime> source) 
{ 
    IEnumerable<DateTime> sourceMonths = 
    source.Select(x => new DateTime(x.Year, x.Month, 1)) 
      .ToList() 
      .Distinct(); 
    return MonthsBetweenInclusive(startDate, endDate).Except(sourceMonths); 
} 

public IEnumerable<DateTime> MonthsBetweenInclusive(
    DateTime startDate, 
    DateTime endDate) 
{ 
    DateTime currentMonth = new DateTime(startDate.Year, startDate.Month, 1); 
    DateTime endMonth = new DateTime(endDate.Year, endDate.Month, 1); 

    while(currentMonth <= endMonth) 
    { 
    yield return currentMonth; 
    currentMonth = currentMonth.AddMonths(1); 
    } 
} 
+0

Что такое «AllMonths»? И не будет ли AllMonths возвращать коллекцию DateTime, чтобы соответствовать типу возвращаемого метода GetMissingMonths? И не будет таким. За исключением работы, поскольку вы выбираете только месяц? –

+0

Упс, переименованный метод и не исправляющий вызов. Исправлена. –

0

Благодаря Яни +1 к его идее. Это одна строка кода :)

void Main() 
{ 

var list = new List<DateTime> 
{ 
    new DateTime(2005, 10, 11), 
    new DateTime(2009, 3, 4), 
    new DateTime(2010, 5, 8), 
    new DateTime(2010, 8, 10), 
    DateTime.Now, 
    new DateTime(2010, 4, 8) 
}; 

     var result= Enumerable.Range(list.Min (l => l.Year), list.Max (l => l.Year) - list.Min (l => l.Year)). 
      SelectMany (e => Enumerable.Range(1, 12).Select (en => new DateTime(e, en, 1))). 
      Except(list.Select(o => new DateTime(o.Year, o.Month, 1))). 
      Where (o => o.Date > list.Min (l => l.Date) && o.Date < list.Max (l => new DateTime(l.Year, l.Month, 1))); 

    } 
+0

Это не сработает, если ваш набор данных содержит пробел в течение года. –

+0

@Iain. Спасибо за указание. Я починил это. Проверь это. –

+0

Возникает проблема, если она почти нечитаема, поэтому ее сложнее поддерживать. Круто, что это одна линия. –

0

Я заброшу свою шляпу, потому что это весело. и я не видел, чтобы кто-то добавлял DateTime в обычный цикл, который мне никогда не удастся сделать, так что снова ... весело.

IEnumerable<DateTime> FindMissingMonths(DateTime startDate, DateTime endDate, IEnumerable<DateTime> inputs) 
{ 
    var allMonths = new List<DateTime>(); 
    for (DateTime d = startDate; d < endDate; d = d.AddMonths(1)) 
    { 
     allMonths.Add(new DateTime(d.Year, d.Month, 1)); 
    } 
    var usedMonths = (from d in inputs 
         select new DateTime(d.Year, d.Month, 1)).Distinct(); 
    return allMonths.Except(usedMonths); 
} 

исправлена ​​ошибка, протестирована, работает.

+0

Не то, что отличается от моего пути ... Мне это нравится, хотя .. сложный linq иногда может усложнять работу. –

Смежные вопросы