如何从列表中选择每个第6个元素(使用Linq)

我有一个’双’值列表。 我需要选择每第6条记录。 这是一个坐标列表,我需要获得每第6个值的最小值和最大值。

坐标列表(样本): [2.1, 4.3, 1.0, 7.1, 10.6, 39.23, 0.5, ... ] 2.1,4.3,1.0,7.1,10.6,39.23,0.5 [2.1, 4.3, 1.0, 7.1, 10.6, 39.23, 0.5, ... ]带有坐标的坐标。

结果应如下所示: [x_min, y_min, z_min, x_max, y_max, z_max] ,正好有6个坐标。

以下代码有效,但需要很长时间才能遍历所有坐标。 我想用Linq代替(也许更快?)

 for (int i = 0; i < 6; i++) { List coordinateRange = new List(); for (int j = i; j < allCoordinates.Count(); j = j + 6) coordinateRange.Add(allCoordinates[j]); if (i < 3) boundingBox.Add(coordinateRange.Min()); else boundingBox.Add(coordinateRange.Max()); } 

有什么建议? 非常感谢! 映入眼帘!

 coordinateRange.Where( ( coordinate, index ) => (index + 1) % 6 == 0 ); 

Webleeuw的回答是在此之前发布的,但恕我直言,使用索引作为参数而不是使用IndexOf方法更清楚。

Where方法的重载允许您直接使用索引:

 coordinateRange.Where((c,i) => (i + 1) % 6 == 0); 

这样的事情可能会有所帮助:

 public static IEnumerable Every(this IEnumerable source, int count) { int cnt = 0; foreach(T item in source) { cnt++; if (cnt == count) { cnt = 0; yield return item; } } } 

你可以像这样使用它:

  int[] list = new []{1,2,3,4,5,6,7,8,9,10,11,12,13}; foreach(int i in list.Every(3)) { Console.WriteLine(i); } 

编辑

如果要跳过前几个条目,可以使用Skip()扩展方法:

 foreach (int i in list.Skip(2).Every(6)) { Console.WriteLine(i); } 

您想使用LINQ执行此操作的任何特定原因?

为什么不写一个循环,每次以6个增量步进,并直接访问该值?

要找到更快的解决方案,请启动配置文件

测量for循环中每个步骤所需的时间,并尽量避免最大的瓶颈。

看了你的代码之后,你的问题就是你在你的大清单上运行了六次。 所以所需的时间总是列表大小的六倍。

相反,您应该在整个列表上运行一次,并将每个项目放入正确的插槽中。

只是为自己做一个性能测试,你应该测试这两种方法:

用于保存数据的示例类

 public class Coordinates { public double x1 { get; set; } public double x2 { get; set; } public double y1 { get; set; } public double y2 { get; set; } public double z1 { get; set; } public double z2 { get; set; } } 

初始化价值持有人

 Coordinates minCoordinates = new Coordinates(); //Cause we want to hold the minimum value, it will be initialized with //value that is definitely greater or equal than the greatest in the list minCoordinates.x1 = Double.MaxValue; minCoordinates.x2 = Double.MaxValue; minCoordinates.y1 = Double.MaxValue; minCoordinates.y2 = Double.MaxValue; minCoordinates.z1 = Double.MaxValue; minCoordinates.z2 = Double.MaxValue; 

如果索引运算符为O(1),则使用for循环

 for (int i = 0; i < allCoordinates.Count; i++) { switch (i % 6) { case 0: minCoordinates.x1 = Math.Min(minCoordinates.x1, allCoordinates[i]); break; case 1: minCoordinates.x2 = Math.Min(minCoordinates.x2, allCoordinates[i]); break; case 2: minCoordinates.y1 = Math.Min(minCoordinates.y1, allCoordinates[i]); break; case 3: minCoordinates.y2 = Math.Min(minCoordinates.y2, allCoordinates[i]); break; case 4: minCoordinates.z1 = Math.Min(minCoordinates.z1, allCoordinates[i]); break; case 5: minCoordinates.z2 = Math.Min(minCoordinates.z2, allCoordinates[i]); break; } } 

如果IEnumerator为O(1),则使用foreach

 int count = 0; foreach (var item in allCoordinates) { switch (count % 6) { case 0: minCoordinates.x1 = Math.Min(minCoordinates.x1, item); break; case 1: minCoordinates.x2 = Math.Min(minCoordinates.x2, item); break; case 2: minCoordinates.y1 = Math.Min(minCoordinates.y1, item); break; case 3: minCoordinates.y2 = Math.Min(minCoordinates.y2, item); break; case 4: minCoordinates.z1 = Math.Min(minCoordinates.z1, item); break; case 5: minCoordinates.z2 = Math.Min(minCoordinates.z2, item); break; } count++; } 

好吧,它不是LINQ,但如果你担心性能,这可能会有所帮助。

 public static class ListExtensions { public static IEnumerable Every(this IList list, int stepSize, int startIndex) { if (stepSize <= 0) throw new ArgumentException(); for (int i = startIndex; i < list.Count; i += stepSize) yield return list[i]; } } 

建议:

coordinateRange.Where(c =>(coordinateRange.IndexOf(c)+ 1)%6 == 0);

我的立场得到了纠正,感谢您的评论。 正如codymanix所说,正确的答案确实如下:

 coordinateRange.Where((c, i) => (i + 1) % 6 == 0); 

执行此操作的最佳方法是重构数据结构,以便每个维度都是自己的数组。 那样x1_max就是x1.Max() 。 如果您无法更改输入数据结构,则下一个最佳方法是迭代数组一次并在本地执行所有访问。 这有助于保持在L1缓存的内存中:

 var minValues = new double[] { Double.MaxValue, Double.MaxValue, Double.MaxValue }; var maxValues = new double[] { Double.MinValue, Double.MinValue, Double.MinValue }; for (int i = 0; i < allCoordinates.Length; i += 6) { for (int j = 0; i < 3; i++) { if (allCoordinates[i+j] < minValues[j]) minValues[j] = allCoordinates[i+j]; if (allCoordinates[i+j+3] > maxValues[j]) maxValues[j] = allCoordinates[i+j+3]; } }