我有一个IEnumerable<T>
包含其中一个属性中具有一致间隔的数据元素列表:
List<Interval> list = new List<Interval>
{
new Interval{ TIME_KEY = 600},
new Interval{ TIME_KEY = 605},
new Interval{ TIME_KEY = 615},
new Interval{ TIME_KEY = 620},
new Interval{ TIME_KEY = 630}
};
如何查询此列表(最好使用Linq),以获得如下所示的列表:
List<Interval> list = new List<Interval>
{
new Interval{ TIME_KEY = 610},
new Interval{ TIME_KEY = 625}
};
编辑:我可能知道间隔距离应该是什么,但是如果有办法通过检查数据来确定它,那将是一个巨大的奖励!
编辑:更改为数值
答案 0 :(得分:3)
一种有效而简单的方法就是通过foreach
查看该列表并检测间隙
我认为5分钟的节拍是固定的吗?
要使用LINQ,您可以创建完整列表并找到差异,但这似乎有点过分。
考虑第2部分,确定间隔:
从您的示例中,可能会有3或4个值的样本。但是,即使在检查了所有值后,您也无法绝对确定。您的示例数据不会排除具有大量缺失值的1分钟频率。
因此,您需要有关此部分的非常好的规格。
答案 1 :(得分:3)
查看this question选择连续值的扩展方法。从那里,你可以做类似的事情:
// I'd probably rename SelectBetween to SelectConsecutive
list.SelectConsecutive((x, y) => new { Original = x, Interval = y - x})
.Where(pair => pair.Interval != 5)
.Select(pair => new Interval(pair.Original + 5));
(有些伪代码,但我希望你能看到我要去的地方。)
但是,只有当它丢失时才会生成一个元素...如果从0到20,它将不会生成5,10,15。
在Henk的第二个建议上添加一些东西:
var missing = Enumerable.Range(0, expectedElementCount)
.Select(x => new Interval(baseInterval + 5 * x)
.Except(list);
答案 2 :(得分:3)
var newList =
Enumerable.Range(0, 6)
.Select(r=> new Interval() {TIME_KEY = ((r*5)+600) })
.Except(list )
答案 3 :(得分:2)
如果您知道间隔,如果您有权访问Zip方法(.NET 4附带),这将有效:
list.Zip(list.Skip(1), (x,y) => new { x, delta = y - x })
.SelectMany(a => Enumerable.Range(1, a.delta/interval - 1)
.Select(i => a.x + i*interval));
请注意,这会迭代列表两次,因此如果源是惰性可枚举的,则需要先缓冲它。使用Zip
和Skip
的构造是将连续元素一起投影的快速而肮脏的方式。 Reactive Extensions'System.Interactive库有Scan
方法,Jon在another answer中展示了可能的实现方式。这些都没有两次迭代列表,所以它们将是一个更好的选择。
如果要确定间隔,您可以获得最小增量:
var deltas = list.Zip(list.Skip(1), (x,y) => y - x );
var interval = deltas.Min();
list.Zip(deltas, (x, delta) => new { x, delta })
.SelectMany(a => Enumerable.Range(1, a.delta/interval - 1)
.Select(i => a.x + i*interval));
我做了一些假设:
工作原理:
a.delta/interval - 1
值,并且每个值都是远离元素存储区的一定数量的间隔。对,因此a.x + i*interval
。SelectMany
负责将所有缺失值序列拼凑成一个。答案 4 :(得分:0)
试试这个:
private static IEnumerable<Interval> CalculateMissingIntervals(IEnumerable<Interval> list, int step) {
return list.Zip(list.Skip(1),
(i1, i2) => IntervalRange(i1.TIME_KEY + step, i2.TIME_KEY, step)).
SelectMany(x => x);
}
private static IEnumerable<Interval> IntervalRange(int start, int end, int step) {
for (var i = start; i < end; i += step) {
yield return new Interval { TIME_KEY = i };
}
}
假设初始列表已排序。
答案 5 :(得分:0)
IEnumerable<Interval> missingIntervals =
Enumerable.Range(list.Min(listMember => listMember.TIME_KEY), list.Max(listMember => listMember.TIME_KEY))
.Where(enumMember => enumMember % intervalDistance == 0)
.Select(enumMember => new Interval { TIME_KEY = enumMember}
.Except(list);