Linq - 通过多个内部属性对对象列表进行排序

3

* 已解决 - 票务更新在底部 *

我有一个"line"对象列表,每个对象都有一个"StartPoint"和一个"EndPoint"属性。这些属性包含标准的点对象,每个点对象都有一个"X"、"Y"和"Z"成分。

使用Linq,我试图首先按照最小的端点Y值升序排序,然后按照最小的端点X值升序排序(当Y值匹配时)。

我认为以下代码会起作用:

var sortedList = linesList
    .OrderBy(o => Math.Min(o.StartPoint.Y, o.EndPoint.Y))
    .ThenBy(o => Math.Min(o.StartPoint.X, o.EndPoint.X));

foreach (Line thisLine in sortedList)
{
    Console.WriteLine(thisLine.StartPoint + ", " + thisLine.EndPoint);
}

然而,这将产生以下顺序(写为xs、ys、zs、xe、ye、ze):
737.928, 825.293, 0, 737.928, 826.293, 0
737.616, 825.293, 0, 737.616, 826.293, 0
733.928, 825.293, 0, 733.928, 826.293, 0
733.616, 825.293, 0, 733.616, 826.293, 0
729.928, 825.293, 0, 729.928, 826.293, 0
729.616, 825.293, 0, 729.616, 826.293, 0
725.928, 825.293, 0, 725.928, 826.293, 0
725.616, 825.293, 0, 725.616, 826.293, 0
721.928, 825.293, 0, 721.928, 826.293, 0
721.616, 825.293, 0, 721.616, 826.293, 0
717.928, 825.293, 0, 717.928, 826.293, 0
717.616, 825.293, 0, 717.616, 826.293, 0
713.928, 825.293, 0, 713.928, 826.293, 0
713.616, 825.293, 0, 713.616, 826.293, 0
709.928, 825.293, 0, 709.928, 826.293, 0
709.616, 825.293, 0, 709.616, 826.293, 0
705.928, 825.293, 0, 705.928, 826.293, 0
705.616, 825.293, 0, 705.616, 826.293, 0
701.928, 825.293, 0, 701.928, 826.293, 0
701.616, 825.293, 0, 701.616, 826.293, 0
697.928, 825.293, 0, 697.928, 826.293, 0
697.616, 825.293, 0, 697.616, 826.293, 0
693.928, 825.293, 0, 693.928, 826.293, 0
693.616, 825.293, 0, 693.616, 826.293, 0
689.928, 825.293, 0, 689.928, 826.293, 0
689.616, 825.293, 0, 689.616, 826.293, 0
685.928, 825.293, 0, 685.928, 826.293, 0
685.616, 825.293, 0, 685.616, 826.293, 0
681.928, 825.293, 0, 681.928, 826.293, 0
681.616, 825.293, 0, 681.616, 826.293, 0
677.928, 825.293, 0, 677.928, 826.293, 0
677.616, 825.293, 0, 677.616, 826.293, 0
673.928, 825.293, 0, 673.928, 826.293, 0
673.616, 825.293, 0, 673.616, 826.293, 0
669.928, 825.293, 0, 669.928, 826.293, 0
669.616, 825.293, 0, 669.616, 826.293, 0
737.928, 826.481, 0, 737.928, 827.481, 0
737.616, 826.481, 0, 737.616, 827.481, 0
733.928, 826.481, 0, 733.928, 827.481, 0
733.616, 826.481, 0, 733.616, 827.481, 0
729.928, 826.481, 0, 729.928, 827.481, 0
729.616, 826.481, 0, 729.616, 827.481, 0
.
.
.

正如您所看到的,"Y"值正确地按升序排列,但"X"值是按降序排列的。 * 更新 ** 在评论区中提出了OrderBy比较器中典型的浮点数容差问题可能性。为了进行快速测试,我将.OrderBy子句更改为以下内容:
.OrderBy(o => Math.Min(Math.Round(o.StartPoint.Y, 3), Math.Round(o.EndPoint.Y, 3)))
这很丑陋,但它解决了排序问题。显然,没有一个"Y"值被视为相等。现在,我强制将它们四舍五入到3位小数,值确实匹配,然后执行ThenBy子句以按"X"值进行排序。
现在,有什么更好的方法来强制匹配吗?编写自己的比较器?

1
你能用一个简短但完整的程序展示一下吗?你确定 X 值中没有发生否定的情况吗? - Jon Skeet
请提供表示StartPoint和EndPoint属性类型的类的名称和起源。它是由您编写的,还是来自互联网上的某个人或Microsoft?如果您有源代码,请提供ToString方法并自行查看。 ToString方法是否尊重X、Y和Z的顺序?如果您没有源代码,则是否阅读了围绕该类及其附属库的文档? - Eduard Dumitru
@JeffGodfrey,你能否尝试在Line类中使用decimal类型来代替当前使用的类型作为XY的数据类型? - King King
@Eduard - 提供“Line”对象和“Point3D”对象的库是第三方商业CAD库。在这种情况下,我没有源代码。虽然我相对有信心Point3D.ToString()保持正确的X,Y,Z顺序,但我肯定会验证一下。 - Jeff Godfrey
这只是一个具有三个PointF属性的类实例集合吗? - Tony Hopkinson
显示剩余9条评论
1个回答

0

我来回答这个更新的问题:

如果你想根据四舍五入后的值进行自定义比较而不修改实际值,你可以实现 IComparer<T>。然后使用接受 IComparer 的 OrderBy<T> 重载方法

public class NonExactLineComparer : IComparer<Line>
{
    public int Compare(Line x, Line y)
    {
        // Comparison logic
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接