我有按日期排序的价格清单。我需要选择所有单调递减的值。以下代码有效:
public static List<DataPoint> SelectDecreasingValues(List<DataPoint> dataPoints)
{
var ret = new List<DataPoint>(dataPoints.Count);
var previousPrice = dataPoints[0].Price;
for (int i = 0; i < dataPoints.Count; i++)
{
if (dataPoints[i].Price <= previousPrice)
{
ret.Add(dataPoints[i]);
previousPrice = dataPoints[i].Price;
}
}
return ret;
}
然而,使用Linq是否有更短/更清晰的方法来完成它?
答案 0 :(得分:3)
此代码等同于:
previousPrice = dataPoints[0].Price;
var ret = dataPoints.Where(x => {
if(x.Price <= previousPrice)
{ previousPrice = x.Price; return true;}
return false;
}).ToList();
但是,如果您不需要列表,请使用普通枚举,然后删除ToList
。这样,您就可以使用LINQ中内置的延迟执行功能。
以下代码也是等效的:
DataPoint previous = dataPoints.FirstOrDefault();
var ret = dataPoints.Where(x => x.Price <= previous.Price)
.Select(x => previous = x).ToList();
这是因为LINQ中的延迟执行。对于dataPoints
中的每个项目,它将首先执行Where
部分,然后执行Select
部分,然后才会移至dataPoints
中的第二个项目。
您需要确定要使用的版本。第二个并不像第一个那样具有意图,因为你需要了解LINQ的内部工作原理。
答案 1 :(得分:2)
public IEnumerable<T> WhereMonotonicDecreasing<T>(
IEnumerable<T> source,
Func<T, IComparable> keySelector)
{
IComparable key;
bool first = true;
foreach(T t in source)
{
if (first)
{
key = keySelector(t);
yield return t;
first = false;
}
else
{
IComparable newKey = keySelector(t);
if (newKey.CompareTo(key) < 0)
{
key = newKey;
yield return t;
}
}
}
}
被叫:
dataPoints.WhereMonotonicDecreasing(x => x.Price);
答案 2 :(得分:0)
previousPrice = dataPoints[0];
dataPoints.Where(p => p.Price <= previousPrice.Price)
.Select(p => previousPrice = p);
如果确实需要,可以使用.ToList()
。
答案 3 :(得分:0)
如何(未经测试):
return dataPoints.Take(1)
.Concat(dataPoints.Skip(1)
.Zip(dataPoints,
(next, previous) =>
new { Next = next, Previous = previous })
.Where(a => a.Next.Price <= a.Previous.Price)
.Select(a => a.Next))
.ToList();
基本上,这会在序列上覆盖序列的“一个延迟”版本,以生成“next,previous”元组,然后在这些元组上应用相关的过滤器。 Take(1)
是选择序列的第一项,它似乎总是需要。
如果您不关心变量名的可读性,可以将其缩短为:
return dataPoints.Take(1)
.Concat(dataPoints.Skip(1)
.Zip(dataPoints, Tuple.Create)
.Where(a => a.Item1.Price <= a.Item2.Price)
.Select(a => a.Item1))
.ToList();