2016-08-29 6 views
1
Mylist.GroupBy(x => new{x.X, x.Y}).Select(g => g.First()).ToList<XYZ>(); 

Приведенный выше код отлично подходит для меня. Я хочу сравнить только точки, основанные на раунде (5) точечного компонента.Удалить дубликаты в списке точек XYZ

Например, 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(); 
+1

Проблема кажется довольно прямой - что вы пробовали, и где именно у вас проблемы? –

+0

Просто добавьте соответствующее округление в 'new {x.X, ...}' – CSharpie

+0

, некоторые дубликаты были удалены, где три компонента x, y, z для двух точек, как правило, одинаковы. но когда, например, если x = 1.1111111111109 и x = 1.1111111111108, эти две точки остаются, поэтому я спрашиваю, могу ли я добавить условие раунда в том же самом заявлении выше! –

ответ

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 и переопределить Equals и GetHashCode - (равно будет делать округления) - не хотел бы предложить

  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)); 
    
+0

Отличие не сработало в начале, и я чувствую, что Comparer немного запутан, так как это вызвало ошибку, когда я передал ее в список. Экземпляры (сравнительный) .tolist() –

+0

@IbrahemHamdyNaeem - проверял первый и последний варианты сейчас -they work :) –

+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

Это лучше подходит для удаления дубликатов.

+0

cool :) Мне это нравится. (И, кстати, я предложил не «GroupBy», а «Distinct» –

+0

. Это действительно хороший момент, который мне пришлось изучить. Спасибо, @ Энигматичность для этого разъяснения, я рассмотрю это в устранении ненужных промежуточных дополнительных когда он реализует это, я напишу здесь. –

Смежные вопросы