删除XYZ点列表中的重复项

时间:2016-08-29 11:49:00

标签: c# linq group-by

Mylist.GroupBy(x => new{x.X, x.Y}).Select(g => g.First()).ToList<XYZ>();

上面的代码对我来说很好。我只想比较基于点分量的圆(5)的点。

例如,x.X = 16.838974347323224应仅作为x.X = 16.83897进行比较,因为我在第5轮后遇到了一些不准确之处。有任何建议吗?

解决方案:

Mylist.GroupBy(x => new { X = Math.Round(x.X,5), Y = Math.Round(x.Y,5) })
               .Select(g => g.First()).ToList();

2 个答案:

答案 0 :(得分:4)

使用Round可能会造成这样的情况,即两个数字即使彼此非常接近,也可能最终被认为是不同的。

举个例子:

var Mylist = new []
{
    new { X = 1.0000051, Y = 1.0 },
    new { X = 1.0000049, Y = 1.0 },
    new { X = 1.1, Y = 1.0 },
    new { X = 1.0, Y = 1.005 },
};

前两个值非常接近 - 实际上它们在小数点后6位有所不同。

如果我们运行此代码:

var result =
    Mylist
        .GroupBy(x => new
        {
            X = Math.Round(x.X,5, MidpointRounding.AwayFromZero),
            Y = Math.Round(x.Y,5, MidpointRounding.AwayFromZero)
        })
        .Select(g => g.First())
        .ToList();

结果是:

result1

舍入允许保留这两个值。

正确的方法是按距离过滤。如果后续值在先前值的阈值内,则应将其丢弃。

这是执行该操作的代码:

var threshold = 0.000001;
Func<double, double, double, double, double> distance
    = (x0, y0, x1, y1) =>
        Math.Sqrt(Math.Pow(x1 - x0, 2.0) + Math.Pow(y1 - y0, 2.0));

var result = Mylist.Skip(1).Aggregate(Mylist.Take(1).ToList(), (xys, xy) =>
{
    if (xys.All(xy2 => distance(xy.X, xy.Y, xy2.X, xy2.Y) >= threshold))
    {
        xys.Add(xy);
    }
    return xys;
});

现在,如果我们在Mylist数据上运行,我们就会得到这个:

result2

这是删除重复项的更好理想。

答案 1 :(得分:0)

为此,请使用Math.Round

var result = Mylist.GroupBy(x => new { X = Math.Round(x.X,5, MidpointRounding.AwayFromZero), Y = Math.Round(x.Y,5, MidpointRounding.AwayFromZero) })
                   .Select(g => g.First()).ToList();

但是,如果你想要的是删除重复项,那么而不是GroupBy去其中一个:

  1. Select四舍五入,然后Distinct

    var result = Mylist.Select(item => new XYZ { X = Math.Round(item.X,5, MidpointRounding.AwayFromZero), 
                                                 Y = Math.Round(item.Y,5, MidpointRounding.AwayFromZero)})
                       .Distinct().ToList();
    
  2. Distinct并覆盖EqualsGetHashCode - (等于将进行四舍五入) - 不建议

  3. Distinct并实施自定义IEqualityComparer

    public class RoundedXyzComparer : IEqualityComparer<XYZ>
    {
        public int RoundingDigits { get; set; }
        public RoundedXyzComparer(int roundingDigits)
        {
           RoundingDigits = roundingDigits;
        }
    
        public bool Equals(XYZ x, XYZ y)
        {
           return Math.Round(x.X, RoundingDigits, MidpointRounding.AwayFromZero) == Math.Round(y.X, RoundingDigits, MidpointRounding.AwayFromZero) && 
                  Math.Round(x.Y,RoundingDigits, MidpointRounding.AwayFromZero) == Math.Round(y.Y, RoundingDigits, MidpointRounding.AwayFromZero);
        }
    
        public int GetHashCode(XYZ obj)
        {
           return Math.Round(obj.X, RoundingDigits, MidpointRounding.AwayFromZero).GetHashCode() ^
                  Math.Round(obj.Y, RoundingDigits, MidpointRounding.AwayFromZero).GetHashCode();
        }
     }
    
     //Use:
     myList.Distinct(new RoundedXyzComparer(5));