我有一个包含日期和值的对象列表。每个日期有一个对象,过去几个月的每个日期都有一个对象。我正在寻找将值更改为最新值的日期。
这是我的意思的一个例子:
<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时调用First()时,执行OrderByDesc,然后在查找lastRecentChange时调用Last()时再次执行它。那么这是否意味着我仍在执行OrderByDesc两次?
我是否正确地解释了延迟执行的运作方式?我希望编译器能够识别这种情况并在幕后进行优化,以便只执行一次执行,但我找不到任何支持这种理论的信息。您能否帮助我了解优化此解决方案的最佳方法?
答案 0 :(得分:3)
这是否意味着我仍在执行OrderByDesc两次?
是的,这是正确的。
我希望编译器能够识别这种情况并在幕后进行优化,以便只执行一次执行,但我找不到任何支持这种理论的信息。
它不能,因为这会以几种关键方式改变预期的功能。
如果基础数据发生变化,则在再次迭代序列时应反映这些变化。如果您在第一个查询和第二个查询之间向dateValues
添加了一个新项,那么它应该在第二个查询中。如果您删除了某个项目,则该项目不应该存在,等等。
要获得你所要求的东西,即使在第一个消费者“完成”之后,也需要将所有项目存储在某种集合中。这是不可取的。这里的想法是你可以流式传输数据,一旦你完成了一个项目的处理,你就完成了它,并且不需要将它保存在内存中。如果您没有足够的内存来保留查询中的所有项目以进行后续运行,该怎么办?
您能否帮我解决优化此解决方案的最佳方法?
这是非常微不足道的。只需使用查询结果填充数据结构即可。最简单的方法是将它们全部放入列表中。在查询末尾添加ToList
调用,它将对其进行一次评估,然后可以多次迭代结果列表而不会产生负面后果。由于这个解决方案,当需要这样的语义时,很容易获得,而延迟执行的语义更难获得,尽管它们更强大,但他们选择不在物化集合上建立LINQ。
答案 1 :(得分:0)
不,如果您使用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);