Created
November 22, 2011 06:52
-
-
Save litera/1385081 to your computer and use it in GitHub Desktop.
Range data lookups using foreach loops and LINQ expressions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace RangeTester | |
{ | |
/// <summary> | |
/// Defines range related members to implementing classes. | |
/// </summary> | |
/// <typeparam name="TPoint">The type of the range point.</typeparam> | |
public interface IRangeChecker<TPoint> | |
{ | |
/// <summary> | |
/// Gets the range starting point. | |
/// </summary> | |
/// <value>Range starting point.</value> | |
TPoint RangeStart { get; } | |
/// <summary> | |
/// Gets the range endpoint. | |
/// </summary> | |
/// <value>Range endpoint.</value> | |
TPoint RangeEnd { get; } | |
/// <summary> | |
/// Gets the range length. | |
/// </summary> | |
/// <value>Range length.</value> | |
int RangeLength { get; } | |
/// <summary> | |
/// Determines whether range point is within this range instance. | |
/// </summary> | |
/// <param name="rangePoint">The <typeparamref name="TPoint"/> range point object instance.</param> | |
/// <returns> | |
/// <c>true</c> if point is within this range; <c>false</c> otherwise. | |
/// </returns> | |
bool IsWithinRange(TPoint rangePoint); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace RangeTester | |
{ | |
/// <summary> | |
/// Defines method for date range lookup classes. | |
/// </summary> | |
/// <typeparam name="TPoint">The type of the point.</typeparam> | |
/// <typeparam name="TResult">The type of the result.</typeparam> | |
public interface IRangeLookup<TPoint, TResult> | |
{ | |
/// <summary> | |
/// Looks up for data that is applicable on provided range point. | |
/// </summary> | |
/// <param name="point">The point.</param> | |
/// <returns> | |
/// Returns a <typeparamref name="TResult"/> object with data that is relevant for the day provided. | |
/// </returns> | |
TResult GetPointData(TPoint point); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
namespace RangeTester | |
{ | |
public class IterationRangeLookupSingle<TItem, TKey> : IRangeLookup<DateTime, TItem>, IRangeChecker<DateTime> | |
where TItem : class, IRangeChecker<DateTime> | |
where TKey : IEquatable<TKey> | |
{ | |
public DateTime RangeStart { get; private set; } | |
public DateTime RangeEnd { get; private set; } | |
public int RangeLength { get { return (int)(this.RangeEnd - this.RangeStart).TotalDays; } } | |
private IList<TItem> items; | |
#region Constructor | |
public IterationRangeLookupSingle(DateTime rangeStart, DateTime rangeEnd, IList<TItem> items, Func<TItem, TKey> keySelector) | |
{ | |
this.RangeStart = RangeStart; | |
this.RangeEnd = rangeEnd; | |
this.items = items.OrderByDescending(keySelector).ToList(); | |
} | |
#endregion | |
#region IRangeLookup<DateTime, TItem> Members | |
public TItem GetPointData(DateTime point) | |
{ | |
//Func<TItem, bool> predicate = i => i.IsWithinRange(point); | |
//foreach (TItem i in this.items) | |
//{ | |
// if (i.IsWithinRange(point)) // predicate(point)) | |
// { | |
// return i; | |
// } | |
//} | |
//return null; | |
TItem cached; | |
for (int i = 0; i < this.items.Count; i++) | |
{ | |
cached = items[i]; | |
if (cached.IsWithinRange(point)) | |
{ | |
return cached; | |
} | |
} | |
return null; | |
} | |
#endregion | |
#region IRangeChecker<DateTime> Members | |
public bool IsWithinRange(DateTime rangePoint) | |
{ | |
return rangePoint >= this.RangeStart && rangePoint < this.RangeEnd; | |
} | |
#endregion | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
namespace RangeTester | |
{ | |
public class LinqRangeLookupSingle<TItem, TKey> : IRangeLookup<DateTime, TItem>, IRangeChecker<DateTime> | |
where TItem : class, IRangeChecker<DateTime> | |
where TKey : IEquatable<TKey> | |
{ | |
public DateTime RangeStart { get; private set; } | |
public DateTime RangeEnd { get; private set; } | |
public int RangeLength { get { return (int)(this.RangeEnd - this.RangeStart).TotalDays; } } | |
private IList<TItem> items; | |
#region Constructor | |
public LinqRangeLookupSingle(DateTime rangeStart, DateTime rangeEnd, IList<TItem> items, Func<TItem, TKey> keySelector) | |
{ | |
this.RangeStart = RangeStart; | |
this.RangeEnd = rangeEnd; | |
this.items = items.OrderByDescending(keySelector).ToList(); | |
} | |
#endregion | |
#region IRangeLookup<DateTime, TItem> Members | |
public TItem GetPointData(DateTime point) | |
{ | |
return this.items.FirstOrDefault(i => i.IsWithinRange(point)); | |
} | |
#endregion | |
#region IRangeChecker<DateTime> Members | |
public bool IsWithinRange(DateTime rangePoint) | |
{ | |
return rangePoint >= this.RangeStart && rangePoint < this.RangeEnd; | |
} | |
#endregion | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Text; | |
namespace RangeTester | |
{ | |
class Program | |
{ | |
private static Random rand = new Random(); | |
private static readonly DateTime RangeStart = new DateTime(2012, 1, 1); | |
private const int RangeCount = 10; | |
private const int RangeLength = 60; | |
private const int IterationCount = 1000000; | |
static void Main(string[] args) | |
{ | |
DateTime starter = new DateTime(2012, 1, 1); | |
IList<Ranger> ranges; | |
Console.OutputEncoding = Encoding.UTF8; | |
do | |
{ | |
ranges = GenerateRanges(); | |
Console.Clear(); | |
Console.WriteLine("Generated Ranges:\n"); | |
Console.WriteLine("ID Range 000000000111111111122222222223300000000011111111112222222222"); | |
Console.WriteLine(" 123456789012345678901234567890112345678901234567890123456789"); | |
foreach (Ranger r in ranges.OrderByDescending(r => r.Id)) | |
{ | |
Console.Write("{0:D2} {1:dd.MM.}-{2:dd.MM.}", r.Id, r.RangeStart, r.RangeEnd); | |
Console.Write("|".PadLeft(r.StartOffset + 1)); | |
Console.WriteLine("|".PadLeft(r.RangeLength, '-')); | |
} | |
} | |
while ((byte)Console.ReadKey(true).KeyChar == 27); | |
var singleIter = new IterationRangeLookupSingle<Ranger, int>(RangeStart, RangeStart.AddDays(RangeLength), ranges, r => r.Id); | |
var singleLinq = new LinqRangeLookupSingle<Ranger, int>(RangeStart, RangeStart.AddDays(RangeLength), ranges, r => r.Id); | |
//var singleBits = new BitCountRangeLookupSingle<Ranger, int>(RangeStart, RangeStart.AddDays(RangeLength), ranges, r => r.Id); | |
//var multiIter = new IterationRangeLookupMulti<Ranger, int>(RangeStart, RangeStart.AddDays(RangeLength), ranges); | |
//var multiLinq = new LinqRangeLookupMulti<Ranger, int>(RangeStart, RangeStart.AddDays(RangeLength), ranges); | |
//var multiBits = new BitCountRangeLookupMulti<Ranger, int>(RangeStart, RangeStart.AddDays(RangeLength), ranges, r => r.Id); | |
Console.WriteLine("\nUsing classes..."); | |
Console.WriteLine("- Iteration: {0}ms", PerformLookup(singleIter)); | |
Console.WriteLine("- Linq: {0}ms", PerformLookup(singleLinq)); | |
//Console.WriteLine("- BitCounter: {0}ms", PerformLookup(singleBits)); | |
//Console.WriteLine("\nMultiples..."); | |
//Console.WriteLine("- Iteration: {0}ms", PerformLookup(multiIter)); | |
//Console.WriteLine("- Linq: {0}ms", PerformLookup(multiLinq)); | |
//Console.WriteLine("- BitCounter: {0}ms", PerformLookup(multiBits)); | |
Console.WriteLine("\nManual loops..."); | |
IList<Ranger> items = ranges.OrderByDescending(r => r.Id).ToList(); | |
Stopwatch timer = new Stopwatch(); | |
for (int z = 0; z < 6; z++) | |
{ | |
timer.Reset(); | |
timer.Start(); | |
for (int i = 0; i < IterationCount; i++) | |
{ | |
DateTime day = GetRandomDay(); | |
foreach (Ranger r in items) | |
{ | |
if (r.IsWithinRange(day)) | |
{ | |
break; | |
} | |
} | |
} | |
timer.Stop(); | |
Console.WriteLine("\n- Iter: {0}ms", timer.ElapsedMilliseconds); | |
timer.Reset(); | |
timer.Start(); | |
for (int i = 0; i < IterationCount; i++) | |
{ | |
DateTime day = GetRandomDay(); | |
items.FirstOrDefault(r => r.IsWithinRange(day)); | |
} | |
timer.Stop(); | |
Console.WriteLine("- Linq: {0}ms", timer.ElapsedMilliseconds); | |
} | |
} | |
#region Helpers | |
#region GenerateRanges | |
private static IList<Ranger> GenerateRanges() | |
{ | |
List<Ranger> result = new List<Ranger>(); | |
for (int i = 0; i < RangeCount; i++) | |
{ | |
var off = GetTwoRandomDays(RangeStart); | |
result.Add(new Ranger { | |
Id = i, | |
RangeStart = off[0], | |
RangeEnd = off[1], | |
StartOffset = (int)(off[0] - RangeStart).TotalDays, | |
EndOffset = (int)(RangeStart.AddDays(RangeLength - 1) - off[1]).TotalDays | |
}); | |
} | |
return result; | |
} | |
#endregion | |
#region GetTwoRandomDays | |
private static DateTime[] GetTwoRandomDays(DateTime zeroDay) | |
{ | |
int[] result = (new int[2] { rand.Next(RangeLength), rand.Next(RangeLength) }).OrderBy(i => i).ToArray(); | |
while (result[0] == result[1] || result[1] - result[0] < 3 || result[1] - result[0] > 10) // RangeLength / RangeCount) | |
{ | |
result = (new int[2] { rand.Next(RangeLength), rand.Next(RangeLength) }).OrderBy(i => i).ToArray(); | |
} | |
return new[] { zeroDay.AddDays(result[0]), zeroDay.AddDays(result[1]) }; | |
} | |
#endregion | |
#region GetRandomDay | |
private static DateTime GetRandomDay() | |
{ | |
return RangeStart.AddDays(rand.Next(RangeLength - 1)); | |
} | |
#endregion | |
#region PerformLookup | |
private static long PerformLookup<TItem>(IRangeLookup<DateTime, TItem> lookup) | |
{ | |
Stopwatch timer = new Stopwatch(); | |
timer.Start(); | |
for (int i = 0; i < IterationCount; i++) | |
{ | |
var r = lookup.GetPointData(GetRandomDay()); | |
} | |
timer.Stop(); | |
return timer.ElapsedMilliseconds; | |
} | |
#endregion | |
#endregion | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace RangeTester | |
{ | |
public class Ranger : IRangeChecker<DateTime> | |
{ | |
public int Id { get; set; } | |
public DateTime RangeStart { get; set; } | |
public DateTime RangeEnd { get; set; } | |
public int RangeLength | |
{ | |
get { return (int)(this.RangeEnd - this.RangeStart).TotalDays; } | |
} | |
public int StartOffset { get; set; } | |
public int EndOffset { get; set; } | |
#region IRangeChecker<DateTime> Members | |
public bool IsWithinRange(DateTime rangePoint) | |
{ | |
return rangePoint >= this.RangeStart && rangePoint < this.RangeEnd; | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment