score:6
You can use the Time Period Library for .NET to calculate the gaps:
// ----------------------------------------------------------------------
public void GapFinder()
{
// base periods
TimePeriodCollection basePeriods = new TimePeriodCollection();
basePeriods.Add( new TimeRange( new DateTime( 2012, 1, 1 ), new DateTime( 2012, 1, 10 ) ) );
basePeriods.Add( new TimeRange( new DateTime( 2012, 1, 11 ), new DateTime( 2012, 1, 25 ) ) );
ITimePeriodCollection combinedBasePeriods = new TimePeriodCombiner<TimeRange>().CombinePeriods( basePeriods );
// test periods
TimePeriodCollection testPeriods = new TimePeriodCollection();
testPeriods.Add( new TimeRange( new DateTime( 2012, 1, 2 ), new DateTime( 2012, 1, 7 ) ) );
testPeriods.Add( new TimeRange( new DateTime( 2012, 1, 8 ), new DateTime( 2012, 1, 9 ) ) );
testPeriods.Add( new TimeRange( new DateTime( 2012, 1, 15 ), new DateTime( 2012, 1, 30 ) ) );
ITimePeriodCollection combinedTestPeriods = new TimePeriodCombiner<TimeRange>().CombinePeriods( testPeriods );
// gaps
TimePeriodCollection gaps = new TimePeriodCollection();
foreach ( ITimePeriod basePeriod in combinedBasePeriods )
{
gaps.AddAll( new TimeGapCalculator<TimeRange>().GetGaps( combinedTestPeriods, basePeriod ) );
}
foreach ( ITimePeriod gap in gaps )
{
Console.WriteLine( "Gap: " + gap );
}
} // GapFinder
score:0
this worked too
static void Main(string[] args) {
List<DateRanges> aDateRanges = new List<DateRanges>();
List<DateRanges> bDateRanges = new List<DateRanges>();
aDateRanges.Add(new DateRanges(new DateTime(2012, 1, 1), new DateTime(2012, 1, 10)));
aDateRanges.Add(new DateRanges(new DateTime(2012, 1, 11), new DateTime(2012, 1, 25)));
bDateRanges.Add(new DateRanges(new DateTime(2012, 1, 2), new DateTime(2012, 1, 7)));
bDateRanges.Add(new DateRanges(new DateTime(2012, 1, 8), new DateTime(2012, 1, 9)));
bDateRanges.Add(new DateRanges(new DateTime(2012, 1, 15), new DateTime(2012, 1, 30)));
DisplayDateRanges(GetGaps(aDateRanges, bDateRanges, 0), "Final Gap Fill");
Console.WriteLine("Completed");
Console.Read();
}
public class DateRanges
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public DateRanges(DateTime Start, DateTime End)
{
StartDate = Start;
EndDate = End;
}
}
public static void DisplayDateRanges(List<DateRanges> dateRanges, string title)
{
Console.WriteLine("************************{0}****************************", title);
Console.WriteLine(Environment.NewLine + "New Recursion");
foreach (DateRanges br in dateRanges)
{
Console.WriteLine("Start Date {0} End Date {1}", br.StartDate, br.EndDate);
}
}
public static List<DateRanges> GetGaps(List<DateRanges> aDateRanges, List<DateRanges> bDateRanges, int recursionlevel)
{
List<DateRanges> gapFill = new List<DateRanges>();
List<DateRanges> gapFillTemp = new List<DateRanges>();
List<DateRanges> bDateRangesTemp = new List<DateRanges>(bDateRanges);
Console.WriteLine(Environment.NewLine + "+++++++++++++++++++++++++++++++++++++++++Recursion Level Id {0} +++++++++++++++++++++++++++++++++++++++++", recursionlevel);
DisplayDateRanges(aDateRanges, " A Date Ranges ");
DisplayDateRanges(bDateRanges, " B Date Ranges ");
foreach (DateRanges br in bDateRanges)
{
if (br.StartDate == br.EndDate)
return gapFill;
foreach (DateRanges ar in aDateRanges)
{
if (ar.StartDate == ar.EndDate)
return gapFill;
if (br.StartDate == ar.StartDate && br.EndDate == ar.EndDate)
continue;
else if (br.StartDate >= ar.StartDate && br.EndDate <= ar.EndDate)
{
gapFillTemp.AddRange(GetGaps(new List<DateRanges> { new DateRanges(ar.StartDate, br.StartDate) }, bDateRangesTemp, recursionlevel + 1));
if (gapFillTemp.Count == 0)
{
//gapFillTemp.Add(new DateRanges(ar.StartDate, br.StartDate));
}
bDateRangesTemp.AddRange(gapFillTemp);
gapFill.AddRange(gapFillTemp);
gapFillTemp.Clear();
gapFillTemp.AddRange(GetGaps(new List<DateRanges> { new DateRanges(br.EndDate, ar.EndDate) }, bDateRangesTemp, recursionlevel + 1));
if (gapFillTemp.Count == 0)
{
// gapFillTemp.Add(new DateRanges(br.EndDate, ar.EndDate));
}
bDateRangesTemp.AddRange(gapFillTemp);
gapFill.AddRange(gapFillTemp);
gapFillTemp.Clear();
}
else if (br.StartDate < ar.EndDate && br.EndDate >= ar.EndDate)
{
gapFillTemp.AddRange(GetGaps(new List<DateRanges> { new DateRanges(ar.StartDate, br.StartDate) }, bDateRangesTemp, recursionlevel + 1));
if (gapFillTemp.Count == 0)
{
//gapFillTemp.Add(new DateRanges(ar.StartDate, br.StartDate));
}
bDateRangesTemp.AddRange(gapFillTemp);
gapFill.AddRange(gapFillTemp);
gapFillTemp.Clear();
}
else if (ar.StartDate >= br.StartDate && ar.StartDate < br.EndDate)
{
gapFillTemp.AddRange(GetGaps(new List<DateRanges> { new DateRanges(br.EndDate, ar.EndDate) }, bDateRangesTemp, recursionlevel + 1));
if (gapFillTemp.Count == 0)
{
// gapFillTemp.Add(new DateRanges(br.EndDate, ar.EndDate));
}
bDateRangesTemp.AddRange(gapFillTemp);
gapFill.AddRange(gapFillTemp);
gapFillTemp.Clear();
}
else if (ar.StartDate >= br.StartDate && ar.EndDate <= br.EndDate)
{
// AS----AE
// BS----------BE
//Do Nothing
}
else
{
if (AllowedToAdd(bDateRangesTemp, new DateRanges(ar.StartDate, ar.EndDate)))
{
bDateRangesTemp.Add(new DateRanges(ar.StartDate, ar.EndDate));
gapFill.Add(new DateRanges(ar.StartDate, ar.EndDate));
}
}
}
}
return gapFill;
}
static bool AllowedToAdd(List<DateRanges> bDateRanges, DateRanges newItem)
{
return !bDateRanges.Any(m =>
(m.StartDate < newItem.StartDate &&
newItem.StartDate < (m.EndDate))
||
(m.StartDate < (newItem.EndDate) &&
(newItem.EndDate) <= (m.EndDate))
||
(newItem.StartDate < m.StartDate &&
m.StartDate < (newItem.EndDate))
||
(newItem.StartDate < (m.EndDate) &&
(m.EndDate) <= (newItem.EndDate))
);
}
score:4
The following code satisfies the given constraints of the problem:
Unit Test
static void Main(string[] args)
{
IEnumerable<DateRange> _base = new[] { new DateRange() { Start = DateTime.Parse("1/1/2012"), End = DateTime.Parse("1/10/2012")},
new DateRange() { Start = DateTime.Parse("1/11/2012"), End = DateTime.Parse("1/25/2012")} };
IEnumerable<DateRange> _test = new[] { new DateRange() { Start = DateTime.Parse("1/2/2012"), End = DateTime.Parse("1/7/2012")},
new DateRange() { Start = DateTime.Parse("1/8/2012"), End = DateTime.Parse("1/9/2012")},
new DateRange() { Start = DateTime.Parse("1/15/2012"), End = DateTime.Parse("1/30/2012")} };
IEnumerable<DateRange> _theoreticalGaps = new[] { new DateRange() { Start = DateTime.Parse("1/1/2012"), End = DateTime.Parse("1/2/2012")},
new DateRange() { Start = DateTime.Parse("1/7/2012"), End = DateTime.Parse("1/8/2012")},
new DateRange() { Start = DateTime.Parse("1/9/2012"), End = DateTime.Parse("1/10/2012")},
new DateRange() { Start = DateTime.Parse("1/11/2012"), End = DateTime.Parse("1/15/2012")} };
var gapsInTestNotInBase = FindGaps(_base, _test);
Debug.Assert(Enumerable.SequenceEqual(_theoreticalGaps, gapsInTestNotInBase));
}
FindGaps Method
public static IEnumerable<DateRange> FindGaps(IEnumerable<DateRange> baseCollection, IEnumerable<DateRange> testCollection)
{
var allBaseDates = baseCollection.SelectMany(o => o.GetDiscreetDates())
.Distinct()
.OrderBy(o => o.Ticks);
var missingInTest = (from d in allBaseDates
let inRange = testCollection.Any(o => d.IsInRange(o))
where !inRange
select d).ToArray();
var gaps = missingInTest.Select(o => new DateRange() { Start = o, End = o.AddDays(1) });
gaps = gaps.GroupConsecutive();
return gaps;
}
DateRange Class
public class DateRange
{
protected bool Equals(DateRange other)
{
return Start.Equals(other.Start) && End.Equals(other.End);
}
public override int GetHashCode()
{
unchecked
{
return (Start.GetHashCode()*397) ^ End.GetHashCode();
}
}
public DateTime Start { get; set; }
public DateTime End { get; set; }
public IEnumerable<DateTime> GetDiscreetDates()
{
//Start is not allowed to equal end.
if (Start.Date == End.Date)
throw new ArgumentException("Start cannot equal end.");
var output = new List<DateTime>();
var current = Start.Date;
while (current < End.Date) {
output.Add(current);
current = current.AddDays(1);
}
return output;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((DateRange) obj);
}
}
Extension Methods
public static class Extensions
{
public static bool IsInRange(this DateTime testDate, DateRange range)
{
return range.Start <= testDate && range.End > testDate;
}
public static IEnumerable<DateRange> GroupConsecutive(this IEnumerable<DateRange> input)
{
var current = input.ToArray();
var nextIndex = 0;
//uses lookahead to figure out if gaps are consecutive.
for (int i = 0; i < current.Length - 1; i++) {
//If the next range is consecutive to the current, skip;
if (!current[i].End.IsInRange(current[i + 1])) {
yield return new DateRange()
{
Start = current[nextIndex].Start,
End = current[i].End
};
nextIndex = i + 1;
}
}
//If the last elements were consecutive, pull out the final item.
if (nextIndex != current.Length) {
yield return new DateRange()
{
Start = current[nextIndex].Start,
End = current[current.Length - 1].End
};
}
}
}
score:4
Extension methods
public static class DateRangeEnumerable
{
public static IEnumerable<DateTime> GetDates(this IEnumerable<DateRange> source)
{
var sortedSource = source.OrderBy(r => r.From);
foreach (var range in sortedSource)
{
var d = range.From;
while (d < range.To)
{
yield return d;
d = d.AddDays(1);
}
}
}
public static IEnumerable<DateRange> GetRanges(this IEnumerable<DateTime> source)
{
var sortedSource = source.OrderBy(d => d);
var enumerator = sortedSource.GetEnumerator();
if (!enumerator.MoveNext())
yield break;
DateTime from = enumerator.Current;
DateTime prev = from;
while (true)
{
while (true)
{
if (enumerator.MoveNext())
{
if (enumerator.Current == prev.AddDays(1))
prev = enumerator.Current;
else
break;
}
else
{
yield return new DateRange() { From = from, To = prev.AddDays(1) };
yield break;
}
}
yield return new DateRange() { From = from, To = prev.AddDays(1) };
from = enumerator.Current;
prev = enumerator.Current;
}
}
}
Usage:
var missing = BaseRanges.GetDates().Except(TestRanges.GetDates());
var ranges = missing.GetRanges();
I've tested it on your sample data and returns what it should.
Both extension methods are lazy and reads/returns one element at the time.
I'm pretty sure GetRanges
method can be made much more readable :)
Source: stackoverflow.com
Related Query
- Find Gap Date Ranges from two Set of Date Ranges C#
- How to get gap in date ranges from a period of time
- How to use LINQ to find all combinations of n items from a set of numbers?
- How to get all records within the last Two Days from the current Date using EF 4?
- Linq query - find strings based upon first letter b/w two ranges
- Linq: find similar objects from two different lists
- Find a set number of values from collection
- creating Linq to sqlite dbml from DbLinq source code
- How can I use LINQ and C# to find the oldest date from 3 different files?
- C# - How to find most frequent date from list
- Solr Facets: How to set Facet.Limit to (-1) unlimited in Sitecore.ContentSearch.Linq from code
- How to select two set from collection in linq
- Find duplicates of exactly two items from Array1 in Array2 using LINQ C#
- C# Linq Select two tables select MAX date from one
- Find all items from two different custom lists using LINQ (C#)
- Select distinct and date difference in Linq to SQL from two dataTables
- GroupJoin by date where date in second list between two dates from first
- Find difference from IList<string[]> one and IList<string[]> two using Linq operation
- C# exclude duplicate code from two method returning expression
- How to return two values from single methods and set two parent class property calling function 1 time?
- Find items with same property from list and pick the cheaper of the two items
- Find items from the list based on given date
- IQueryable two tables from Code First Entity Framework
- Find max and min date from different property
- Match data from two list<T> where date from one list falls between dates from second list
- c# Linq or code to extract groups from a single list of source data
- Get records from datatable grouped by date and every two hour
- IQueryable is set to null when trying to OrderBy date property from class
- Find a certain value from a source collection in a target collection of different type with linq
- Grouping a sequence into two sets, then printing from first set based on num of elements in second set using LINQ
More Query from same tag
- Using Linq To Get Data By Date
- Building 'flat' rather than 'tree' LINQ expressions
- How do I update a field of a record using linq
- Linq: Performant database query only querying every nth element
- Filtering Linq based on Enum, with a specific unique value exception
- Is LINQ a valid option?
- Type of one of the expressions in the join clause is incorrect
- Entity Framework - Disctinct by property
- Linq query has a bug?
- Linq Query not returning IEnumerable
- Is there a way to parameterize the Contains portion of Linq when linked to an array
- Possible self join to get id of all matching columns
- query nested objects with LINQ
- Converting list with nested list to a separate list using Linq
- GroupBy and Distinct don't work
- How to take top n categories ordered by date with additional info using LINQ
- C# Linq query execution order
- Successive SelectMany in Linq Request
- Model defined function not recognized
- LiNQ join two tables for substraction SUM
- Doing a Left outer join in a linq query
- Return items in one list based upon their matching index in another using Linq
- Select top1 with Linq
- Performing a Sum() with LINQ query on database
- LinQ Query Dynamic Conditions List
- How to include FirstOrDefault() in ToList()
- LINQ query to query a list with array members
- multiple Orderby and Sort
- how to write custom Linq groupBy
- EF - not supported in LINQ to Entities