某些时间段设置了价格...我在确定特定时间段的最低价格时遇到问题。
我使用对象列表执行此操作,其中对象具有属性DateTime StartDate, DateTime EndDate, decimal Price
。
例如,两个价格集及其有效日期范围:
A. 09/26/16 - 12/31/17 at $20.00
B. 12/01/16 - 12/31/16 at $18.00
您可以看到B在A时段内且较低。
我需要转换成这个:
A. 09/26/16 - 11/30/16 at $20.00
B. 12/01/16 - 12/31/16 at $18.00
C. 01/01/17 - 12/31/17 at $20.00
它必须适用于任意数量的日期范围和组合。有没有人遇到任何我可以操纵以获得我需要的结果的东西?还是有什么建议吗?
编辑:我的数据结构:
public class PromoResult
{
public int ItemId { get; set; }
public decimal PromoPrice { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int PromoType { get; set; } // can ignore this...
}
答案 0 :(得分:3)
这是使用Linq的一个很好的例子。假设您的价格范围对象被称为PriceRecord
...
您需要创建所有日期的列表,然后过滤到两个连续日期之间的价格记录。实现可能如下所示:
public static IEnumerable<PriceRecord> ReduceOverlaps(IEnumerable<PriceRecord> source)
{
// Get a list of all edges of date ranges
// edit, added OrderBy (!)
var edges = source.SelectMany(record => new[] { record.StartDate, record.EndDate }).OrderBy(d => d).ToArray();
// iterate over pairs of edges (i and i-1)
for (int i = 1; i < edges.Length; i++)
{
// select min price for range i-1, i
var price = source.Where(r => r.StartDate <= edges[i - 1] && r.EndDate >= edges[i]).Select(r => r.Price).Min();
// return a new record from i-1, i with price
yield return new PriceRecord() { StartDate = edges[i - 1], EndDate = edges[i], Price = price };
}
}
我没有对此进行测试,您可能需要修改比较运算符,但这可能是一个很好的起点。
我现在测试了代码,此处的示例与问题中的数据一起使用。
随意提出修改以改进此示例。
答案 1 :(得分:1)
没有直接回答你的问题,但这里有一些我用来解决类似问题的SQL(简化了一下,因为我还处理多个地点和不同的价格类型):
SELECT RI.ItemNmbr, RI.UnitPrice, RI.CasePrice
, RP.ProgramID
, Row_Number() OVER (PARTITION BY RI.ItemNmbr,
ORDER BY CASE WHEN RI.UnitPrice > 0
THEN RI.UnitPrice
ELSE 1000000 END ASC
, CASE WHEN RI.CasePrice > 0
THEN RI.CasePrice
ELSE 1000000 END ASC
, RP.EndDate DESC
, RP.BeginDate ASC
, RP.ProgramID ASC) AS RowNumBtl
, Row_Number() OVER (PARTITION BY RI.UnitPrice,
ORDER BY CASE WHEN RI.CasePrice > 0
THEN RI.CasePrice
ELSE 1000000 END ASC
, CASE WHEN RI.UnitPrice > 0
THEN RI.UnitPrice
ELSE 1000000 END ASC
, RP.EndDate DESC
, RP.BeginDate ASC
, RP.ProgramID ASC) AS RowNumCase
FROM RetailPriceProgramItem AS RI
INNER JOIN RetailPriceMaster AS RP
ON RP.ProgramType = RI.ProgramType AND RP.ProgramID = RI.ProgramID
WHERE RP.ProgramType='S'
AND RP.BeginDate <= @date AND RP.EndDate >= @date
AND RI.Active=1
我从UnitPrice的RowNumBtl = 1和CasePrice的RowNumCase = 1中选择。如果您随后创建了一个日期表(您可以使用CTE执行此操作),则可以在每个日期交叉应用。这样效率有点低,因为你只需要在日期范围之间的边界条件下进行测试,所以......祝你好运。
答案 2 :(得分:1)
我将使用2个函数DateRange
和GroupSequenceWhile
List<PromoResult> promoResult = new List<PromoResult>()
{
new PromoResult() { PromoPrice=20, StartDate = new DateTime(2016, 9, 26),EndDate=new DateTime(2017, 12, 31)},
new PromoResult() { PromoPrice=18, StartDate = new DateTime(2016, 12, 1),EndDate=new DateTime(2016, 12, 31)}
};
var result = promoResult.SelectMany(x => DateRange(x.StartDate, x.EndDate, TimeSpan.FromDays(1))
.Select(y => new { promo = x, date = y }))
.GroupBy(x => x.date).Select(x => x.OrderBy(y => y.promo.PromoPrice).First())
.OrderBy(x=>x.date)
.ToList();
var final = result.GroupSequenceWhile((x, y) => x.promo.PromoPrice == y.promo.PromoPrice)
.Select(g => new { start = g.First().date, end = g.Last().date, price = g.First().promo.PromoPrice })
.ToList();
foreach (var r in final)
{
Console.WriteLine(r.price + "$ " + r.start.ToString("MM/dd/yy", CultureInfo.InvariantCulture) + " " + r.end.ToString("MM/dd/yy", CultureInfo.InvariantCulture));
}
<强>输出:强>
20$ 09/26/16 11/30/16
18$ 12/01/16 12/31/16
20$ 01/01/17 12/31/17
<强>算法:强>
1-为<day,price>
列表
promoResult
元组
按天将这个元组分组并选择最低价格
按日期排序此元组
4-选择连续几天价格发生变化的开始和结束日
IEnumerable<DateTime> DateRange(DateTime start, DateTime end, TimeSpan period)
{
for (var dt = start; dt <= end; dt = dt.Add(period))
{
yield return dt;
}
}
public static IEnumerable<IEnumerable<T>> GroupSequenceWhile<T>(this IEnumerable<T> seq, Func<T, T, bool> condition)
{
List<T> list = new List<T>();
using (var en = seq.GetEnumerator())
{
if (en.MoveNext())
{
var prev = en.Current;
list.Add(en.Current);
while (en.MoveNext())
{
if (condition(prev, en.Current))
{
list.Add(en.Current);
}
else
{
yield return list;
list = new List<T>();
list.Add(en.Current);
}
prev = en.Current;
}
if (list.Any())
yield return list;
}
}
}
答案 3 :(得分:0)
我将从基于开始日期的日期顺序开始,将第一个条目全部添加为范围,以便:
09/26/16 - 12/31/17 at $20.00
TBD:
12/01/16 - 12/31/16 at $18.00
接下来抓住你拥有的下一个范围,如果它与前一个范围重叠,则分割重叠(有几种重叠,确保全部处理它们),取重叠区域的最小值:
09/26/16 - 11/30/16 at $20.00
12/01/16 - 12/31/16 at $18.00
TBD:
01/01/17 - 12/31/17 at $20.00
请注意,您还没有最后一个,因为您会接受之后发生的任何拆分,并将它们放回到排序列表中,然后进行比较&#34;项目
答案 4 :(得分:-3)
试试这个
我们说:
public class DatePrice
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public decimal Price { get; set; }
}
和
IList<DatePrice> list = new List<DatePrice>(); // populate your data from the source..
var lowestPriceItem = list.OrderBy(item => item.Price).First();
应该给你最低价格项目。