如何处理多个 Enumerable.Zip 调用?
How to deal with multiple Enumerable.Zip calls?
我有几个带有传感器数据的枚举
time_elapsed、speed_x、speed_y、speed_z、海拔、纬度、经度……
每个列表都有相同数量的元素。
我想将所有列表的数据合并成一个状态项序列。
class Status
{
public int TimeElapsed {get; set; }
public double SpeedX {get; set; }
public double SpeedY {get; set; }
public double SpeedZ {get; set; }
...
}
想过用Enumerable.Zip的方法,但是看起来真的很麻烦:
var statuses = time_elapsed
.Zip(speed_x, (a, b) => new { a, b})
.Zip(speed_y, (c, d) => new { c, d})
.Zip(speed_z, (e, f) => new { e , f})
.Select(x => new Status
{
Time = x.e.c.a,
SpeedX = x.e.c.b,
SpeedY = x.e.d,
SpeedZ = x.f
// ...
});
如您所见,它来自所有这些匿名类型的可读性。
有没有更好的方法来做到这一点而不至于失去理智?
您在这里可以做的不多,但您可以提高可读性并删除一种匿名类型:
var statuses = time_elapsed
.Zip(speed_x, (time, speedX) => new {time, speedX})
.Zip(speed_y, (x, speedY) => new {x.time, x.speedX, speedY})
.Zip(speed_z, (x, speedZ) => new Status
{
TimeElapsed = x.time,
SpeedX = x.speedX,
SpeedY = x.speedY,
SpeedZ = speedZ
});
您也可以使用这种方法:
int minSize = new IList[] {time_elapsed, speed_x, speed_y, speed_z}.Min(c => c.Count);
IEnumerable<Status> statuses = Enumerable.Range(0, minSize)
.Select(i => new Status
{
TimeElapsed = time_elapsed[i],
SpeedX = speed_x[i],
SpeedY = speed_y[i],
SpeedZ = speed_z[i],
});
@Michael Liu 的 link 指出了 Jon Skeet 的一些代码,它为您的问题提供了一个干净的解决方案,它只是创建一个处理 3 个序列的自定义 zip:
static IEnumerable<TResult> Zip<TFirst, TSecond, TThird, TResult>(
IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
IEnumerable<TThird> third,
Func<TFirst, TSecond, TThird, TResult> resultSelector)
{
using (IEnumerator<TFirst> iterator1 = first.GetEnumerator())
using (IEnumerator<TSecond> iterator2 = second.GetEnumerator())
using (IEnumerator<TThird> iterator3 = third.GetEnumerator())
{
while (iterator1.MoveNext() && iterator2.MoveNext() && iterator3.MoveNext())
{
yield return resultSelector(iterator1.Current, iterator2.Current, iterator3.Current);
}
}
}
我有几个带有传感器数据的枚举
time_elapsed、speed_x、speed_y、speed_z、海拔、纬度、经度……
每个列表都有相同数量的元素。
我想将所有列表的数据合并成一个状态项序列。
class Status
{
public int TimeElapsed {get; set; }
public double SpeedX {get; set; }
public double SpeedY {get; set; }
public double SpeedZ {get; set; }
...
}
想过用Enumerable.Zip的方法,但是看起来真的很麻烦:
var statuses = time_elapsed
.Zip(speed_x, (a, b) => new { a, b})
.Zip(speed_y, (c, d) => new { c, d})
.Zip(speed_z, (e, f) => new { e , f})
.Select(x => new Status
{
Time = x.e.c.a,
SpeedX = x.e.c.b,
SpeedY = x.e.d,
SpeedZ = x.f
// ...
});
如您所见,它来自所有这些匿名类型的可读性。
有没有更好的方法来做到这一点而不至于失去理智?
您在这里可以做的不多,但您可以提高可读性并删除一种匿名类型:
var statuses = time_elapsed
.Zip(speed_x, (time, speedX) => new {time, speedX})
.Zip(speed_y, (x, speedY) => new {x.time, x.speedX, speedY})
.Zip(speed_z, (x, speedZ) => new Status
{
TimeElapsed = x.time,
SpeedX = x.speedX,
SpeedY = x.speedY,
SpeedZ = speedZ
});
您也可以使用这种方法:
int minSize = new IList[] {time_elapsed, speed_x, speed_y, speed_z}.Min(c => c.Count);
IEnumerable<Status> statuses = Enumerable.Range(0, minSize)
.Select(i => new Status
{
TimeElapsed = time_elapsed[i],
SpeedX = speed_x[i],
SpeedY = speed_y[i],
SpeedZ = speed_z[i],
});
@Michael Liu 的 link 指出了 Jon Skeet 的一些代码,它为您的问题提供了一个干净的解决方案,它只是创建一个处理 3 个序列的自定义 zip:
static IEnumerable<TResult> Zip<TFirst, TSecond, TThird, TResult>(
IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
IEnumerable<TThird> third,
Func<TFirst, TSecond, TThird, TResult> resultSelector)
{
using (IEnumerator<TFirst> iterator1 = first.GetEnumerator())
using (IEnumerator<TSecond> iterator2 = second.GetEnumerator())
using (IEnumerator<TThird> iterator3 = third.GetEnumerator())
{
while (iterator1.MoveNext() && iterator2.MoveNext() && iterator3.MoveNext())
{
yield return resultSelector(iterator1.Current, iterator2.Current, iterator3.Current);
}
}
}