我有一个包含日期和值的对象列表。过去几个月中,每个日期都有一个对象,每个日期都有一个对象。我正在寻找的日期,该值已更改为最近的价值。
这里是我的意思的一个例子:
<datevalue>
<date>8-9</date>
<value>5</value>
</datevalue>
<datevalue>
<date>8-10</date>
<value>6</value>
</datevalue>
<datevalue>
<date>8-11</date>
<value>5</value>
</datevalue>
<datevalue>
<date>8-12</date>
<value>5</value>
</datevalue>
<datevalue>
<date>8-13</date>
<value>5</value>
</datevalue>在上面的例子中,当前值是5,因为它是8-13上的值,也就是最近的日期。我想返回8-11 datevalue对象,因为这是将值更改为最新值的日期。我不想要8-9值,因为尽管它是当前值最早的一天,但值在那个日期之后被更改了。
这是我第一次尝试解决这个问题:
DateValue FindMostRecentValueChange(List<DateValue> dateValues)
{
var currentValue = dateValues
.OrderByDesc(d => d.date)
.Select(d => d.value)
.First();
var mostRecentChange = dateValues
.OrderByDesc(d => d.date)
.TakeWhile(d => d.value = currentValue)
.Last();
return mostRecentChange;
}这个很管用。但是,有人向我指出,对于这两种操作,我都重复了OrderByDesc。考虑到OrderByDesc可能是一个昂贵的手术,我不想再做两次了。因此,我做了一个改变:
DateValue FindMostRecentValueChange(List<DateValue> dateValues)
{
var orderedDateValues = dateValues.OrderByDesc(d => d.date);
var currentValue = orderedDateValues;
.Select(d => d.value)
.First();
var mostRecentChange = orderedDateValues
.TakeWhile(d => d.value = currentValue)
.Last();
return mostRecentChange;
}现在我只给OrderByDesc打了一次电话。这是个进步,对吧?嗯,也许不是。OrderByDesc是一个延迟执行。
据我所知,这意味着在您要求它的值之前,实际的排序是不会完成的。因此,当在查找currentValue时调用OrderByDesc()时,执行OrderByDesc,然后在查找mostRecentChange时再次执行mostRecentChange。那么,这是否意味着我还在执行OrderByDesc两次?
我是否正确地解释了延迟执行是如何运作的?我希望编译器能够识别这个场景并在幕后对其进行优化,以便只调用一次执行,但是我找不到任何信息来支持这个理论。你能帮我想出优化这个解决方案的最佳方法吗?
发布于 2013-08-13 19:19:41
那么,这是否意味着我还在执行OrderByDesc两次?
是的,这是正确的。
我希望编译器能够识别这个场景并在幕后对其进行优化,以便只调用一次执行,但是我找不到任何信息来支持这个理论。
它不能这样做,因为这将在几个关键方面改变预期的功能。
dateValues添加了一个新项,那么它应该在第二个查询中。如果你移走了一件物品,它就不应该在那里,等等。你能帮我想出优化这个解决方案的最佳方法吗?
这是相当微不足道的。只需使用查询的结果填充数据结构即可。做这件事最简单的方法就是把它们都列在一张清单上。将一个ToList调用添加到查询的末尾,它将对其进行一次计算,然后可以对结果列表进行多次迭代,而不会产生负面后果。因为当需要这种语义时,这个解决方案是很容易获得的,而延迟执行的语义则更难获得,尽管它更强大,所以他们选择不将LINQ建立在物化集合上。
发布于 2013-08-13 19:16:41
不,如果您使用First()或Last()以及其他一些工具,您的查询将被正确执行。这意味着您要调用OrderBy两次(包括OrderByDescending)。
你可以试试这个:
var mostRecentChange = dateValues.OrderBy(d=>d.Date)
.SkipWhile((x,i)=>i==dateValues.Count-1||x.Value == dateValues[i+1].Value)
.Take(1);https://stackoverflow.com/questions/18217149
复制相似问题