#c#
#c#
Вопрос:
Мне нужно проверить, полностью ли диапазон дат покрыт этой таблицей диапазонов дат, отсортированной в порядке возрастания dFrom, оба типа даты:
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
не полностью покрыто, потому что 10/04 отсутствует в таблице.
range B: 10/10-10/20
полностью покрыто.
Что я могу придумать, так это для заданного диапазона дат, такого как A и B, чтобы проверить, покрыт ли каждый день в таблице:
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"] amp;amp; 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");
Комментарии:
1. «C # не имеет типа даты», System. Дата-время?
2.
var
это просто неявный тип, который разрешается компилятором — это не сам фактический тип. Фактический типDateTime
. Если вы наведете курсорvar
, он покажет вам тип во всплывающей подсказке в большинстве IDE.3.
table
dFrom
иdTo
не определены в примере кода. Пожалуйста, опубликуйте минимальный образец, иллюстрирующий проблему.
Ответ №1:
Одним из способов решить эту проблему было бы написать вспомогательный метод, который получает все дни в диапазоне:
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 resu<
}
А затем мы можем объединить все диапазоны из таблицы вместе и посмотреть, соответствуют ли они дням в диапазоне, который мы пытаемся охватить:
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");
}
Комментарии:
1. Потрясающий образец. Я, конечно, буду учиться. Я считаю, что с добавлением оптимизации в мой исходный код производительность и удобочитаемость улучшатся. Спасибо.
Ответ №2:
Наивное решение — проверять для каждой даты в диапазоне, покрыта ли она какой-либо строкой.
var totallyCovered = true;
for (var date = rangeFrom.Date; date <= rangeTo.Date; date = date.AddDays(1))
{
var covered = dates.Any(x => date >= x.dFrom amp;amp; date <= x.dTo);
if (!covered)
{
totallyCovered = false;
break;
}
}
if (totallyCovered)
{
Console.WriteLine("Totally covered.");
}
else
{
Console.WriteLine("No.");
}
Это довольно долго и некрасиво, но, к счастью, вы можете поместить это в один запрос LINQ:
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 amp;amp; d <= x.dTo));
Примечание: это имеет временную сложность O(|range| * |rows|)
, которая может быть слишком большой. Чтобы исправить это, вам пришлось бы использовать более сложную структуру данных, которая позволила бы вам запрашивать диапазоны за логарифмическое время, но поскольку ваш исходный образец также содержал вложенные циклы, я предполагаю, что в этом нет необходимости.
Комментарии:
1. Спасибо за образец. Поскольку LinQ фактически
foreach
находится за сценой, с добавлением 2 оптимизаций в мой исходный код, производительность в порядке. Также я считаю, что любому, кто читает мой код, легче понять алгоритм.2. @Jeb50 Я согласен, производительность была бы проблемой, только если бы у вас были сотни тысяч строк.