public class SalesRecord
{
public DateTime Date { get; set; }
public bool IsValid { get; set; }
public int SalesType { get; set; }
public decimal UnitPrice { get; set; }
}
public class SalesDataContainter
{
private SortedList<DateTime, SalesRecord> salesData;
private decimal GetInferredPrice(SalesRecord inferredSalesRecord)
{
Func<SalesRecord, bool> isValidRecord = record =>
record.IsValid //neigbor needs to be valid
&& record.SalesType == inferredSalesRecord.SalesType; //has to have the same sales type
var candidateRecords = salesData.Values
.SkipWhile(record => !isValidRecord(record))
.Where(isValidRecord);
SalesRecord closestBefore = null;
SalesRecord closestAfter = null;
foreach (var salesRecord in candidateRecords)
{
//get the last found for before neighbor
if (salesRecord.Date < inferredSalesRecord.Date)
{
closestBefore = salesRecord;
}
//get the first found for after neighbor
else if (salesRecord.Date > inferredSalesRecord.Date)
{
closestAfter = salesRecord;
break;
}
}
// there's a neighbor before
if (closestBefore != null)
{
return (closestAfter != null)
// we have neighbors on both sides
? (closestBefore.UnitPrice + closestAfter.UnitPrice) / 2M
: closestBefore.UnitPrice;
}
else
{
return (closestAfter != null)
// there's only a neighbor after
? closestAfter.UnitPrice
: 0M;
}
}
}
An alternative would be to let LINQ do all the work:
public decimal GetInferredPrice(SalesRecord inferredSalesRecord)
{
Func<SalesRecord, bool> isValidRecord = record =>
record.IsValid //neigbor needs to be valid
&& record.SalesType == inferredSalesRecord.SalesType; //has to have the same sales type
return salesData.Values
.SkipWhile(record => !isValidRecord(record))
.Where(isValidRecord)
.GroupBy(
record => record.Date.CompareTo(inferredSalesRecord.Date),
record => record.UnitPrice as decimal?,
(key, g) => (key < 0) // assumes no records will have equal dates
? g.LastOrDefault()
: g.FirstOrDefault()
)
.Average() ?? 0M;
}