Skip to content

Instantly share code, notes, and snippets.

@litera
Created November 22, 2011 06:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save litera/1385081 to your computer and use it in GitHub Desktop.
Save litera/1385081 to your computer and use it in GitHub Desktop.
Range data lookups using foreach loops and LINQ expressions
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);
}
}
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);
}
}
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
}
}
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
}
}
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
}
}
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