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

时间:2010-03-16 11:10:05

标签: c# linq

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

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

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

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

for (int i = 0; i < 6; i++)
{
    List<double> coordinateRange = new List<double>();

    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());
}

有什么建议吗? 非常感谢!迎接!

9 个答案:

答案 0 :(得分:19)

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

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

答案 1 :(得分:6)

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

public static IEnumerable<T> Every<T>(this IEnumerable<T> 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); }

答案 2 :(得分:6)

Where方法有一个重载,让你直接使用索引:

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

答案 3 :(得分:4)

您是否想要使用LINQ执行此操作?

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

答案 4 :(得分:4)

要找到更快的解决方案启动个人资料

衡量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++;
}

答案 5 :(得分:2)

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

public static class ListExtensions
{
    public static IEnumerable<T> Every<T>(this IList<T> list, int stepSize, int startIndex)
    {
        if (stepSize <= 0)
            throw new ArgumentException();

        for (int i = startIndex; i < list.Count; i += stepSize)
            yield return list[i];
    }
}

答案 6 :(得分:1)

建议:

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

我表示反对,感谢您的评论。 正如codymanix所说,正确的答案确实是:

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

答案 7 :(得分:1)

执行此操作的最佳方法是重构数据结构,以便每个维度都是自己的数组。那样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];
    }
}

答案 8 :(得分:0)

与Take一起使用Skip and combintaion。

coordinateRange.Skip(6).Take(1).First();

我建议您将其移至扩展方法。