在C#中查找列表列表的第N个元素的最大值

时间:2013-04-03 13:56:57

标签: c# linq max nested-lists

我在C#中有一个列表列表,其中每个子列表有三个双精度表示3D点:

{{x1, y1, z1},
 {x2, y2, z2},
 {x3, y3, z3}}

我想找到这个数据集的3D边界框,这意味着找到最小X,最大X,最小Y,最大Y等。

使用Python / Numpy,我会得到它,比如,zmax = list_of_lists[:,2].max()等。

在C#中有优雅的方法吗?我怀疑Linq是要走的路,但我还没有理解它是如何工作的(如果一些答案包括Linq,请解释它是如何工作的,请:o)

5 个答案:

答案 0 :(得分:2)

像这样创建列表,如果需要更多功能,可能会使用特定的3D点而不是元组。

var points = new[] {
    Tuple.Create(x1, y1, z1),
    Tuple.Create(x2, y3, z2),
    Tuple.Create(x3, y3, z3)
};

然后,就像@dasblinkenlight所写,您可以使用linq选择Max()Min()

var maxX = points.Select(pt => pt.Item1).Max();

甚至更短:

var maxX = points.Max(pt => pt.Item1);

修改:易于使用的简单类:

class Point3D {
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }

    public Point3D(double x, double y, double z) {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }
}

var points = new[] {
    new Point3D(x1, y1, z1),
    new Point3D(x2, y3, z2),
    new Point3D(x3, y3, z3)
};

var maxX = points.Max(pt => pt.X);

答案 1 :(得分:1)

是的,您可以使用LINQ在C#中执行此操作,如下所示:

var orig = new List<List<double>>();
var maxX = orig.Select(pt => pt[0]).Max();
var maxY = orig.Select(pt => pt[1]).Max();
var maxZ = orig.Select(pt => pt[2]).Max();

这在LINQ中的工作方式是遍历点列表,对于每个点,选择所请求的坐标,然后计算Max。还有一个Min函数可以让你到达边界框的另一个角落。

这有些不理想,因为列表遍历了多次。一对嵌套循环可能会更有效地执行相同的操作,同时保持可读性:

var min = new List<double>{double.MaxValue, double.MaxValue, double.MaxValue};
var max = new List<double>{double.MinValue, double.MinValue, double.MinValue};
foreach (var point in orig) {
    for (var i = 0 ; i != 3 ; i++) {
        min[i] = Math.Min(min[i], point[i]);
        max[i] = Math.Max(max[i], point[i]);
    }
}

答案 2 :(得分:0)

如果所有元素属于同一类型且您感兴趣的属性被命名为Z,则可以执行以下操作:

var max = list_of_points.Max(p => p.Z);

在您的情况下,由于您似乎使用double[][],因此可以执行此操作:

var max = list_of_lists.Max(p => p[0]);

所以完整的边界框将由两个点定义,如下所示:

var topLeft = new double[] 
{
    list_of_lists.Min(p => p[0]),
    list_of_lists.Min(p => p[1]),
    list_of_lists.Min(p => p[2])
};
var bottomRight = new double[] 
{
    list_of_lists.Max(p => p[0]),
    list_of_lists.Max(p => p[1]),
    list_of_lists.Max(p => p[2])
};

但是,这需要您在列表中进行6次传递(每个点的每个坐标一次)。您可以这样做以提高性能:

var topLeft = list_of_lists.Aggregate((s, p) => return double[] { Math.Min(s[0], p[0]), Math.Min(s[1], p[1]), Math.Min(s[2], p[2]) });
var bottomRight = list_of_lists.Aggregate((s, p) => return double[] { Math.Max(s[0], p[0]), Math.Max(s[1], p[1]), Math.Max(s[2], p[2]) });

这将只通过列表2次。

注意:只需在每个数组声明后添加List<List<double>>,就可以使用double[][]而非.ToList()完成相同的代码。

答案 3 :(得分:0)

如果这实际上是IEnumerableIEnumerables的双倍,并且总是具有相同的X,Y,Z顺序,那么你可以这样做:

var list = new []
            {
                new [] { 1, 2, 3 },
                new [] { 4, 5, 6 },
                new [] { 7, 8, 9 }
            };
var maxX = list.Select(s => s.Skip(0).Take(1)).Max();
var minY = list.Select(s => s.Skip(1).Take(1)).Min();

答案 4 :(得分:-1)

您是否看过SelectMany linq运算符?我认为这可能对您的问题有所帮助。