2013-04-26 3 views
0

Допустим, что у меня есть список пользовательских классов «Точка» (я знаю, что есть один в System.Drawing, но позволяет сказать, что мне нужен пользовательский). Теперь этот список может иногда иметь одни и те же точки, так, например, сказать, что настроен так:Поиск отдельных точек в списке (с использованием 2 предикатов)

List<customPoint> myPoints = new List<customPoint>(); 
myPoints.Add(new customPoint(1,5)); 
myPoints.Add(new customPoint(1,5)); 
myPoints.Add(new customPoint(2,3)); 
myPoints.Add(new customPoint(4,9)); 
myPoints.Add(new customPoint(8,7)); 
myPoints.Add(new customPoint(2,3)); 

А потом мне нужно сделать некоторые расчеты, но мне не нужно дубликатов. Что бы более элегантный способ сделать новый список уникальных точек, чем это:

List<customPoint> uniquePoints = new List<customPoint>(); 

for(int i; i < myPoints.Count; i++) 
{ 
    Boolean foundDuplicate = false;  

    int tempX = myPoints[i].X; 
    int tempY = myPoints[i].Y;   

    for(int j=0; j < uniquePoints.Count; j++) 
    { 
     if((tempX == uniquePoints[0].X) && (tempY == uniquePoints[0].Y)) 
     { 
      foundDuplicate = true; 
      break; 
     }    
    } 
    if(!foundDuplicate) 
    { 
     uniquePoints.Add(myPoints[i]); 
    }   
} 

Я знаю, что это грязное дело, но именно поэтому я прошу, если есть более элегантный способ. Я посмотрел на команду Linq «Distinct», но, похоже, она не работает, я думаю, что есть что-то в их экземпляре объектов, которое по-прежнему уникально.

+0

Выполняется ли ваш 'customPo int' class (который должен быть 'CustomPoint', чтобы следовать соглашениям об именах) переопределить' Equals' и 'GetHashCode' соответственно? –

+0

Вот ссылка [Distinct] (http: // stackoverflow.com/questions/998066/linq-distinct-values) к предыдущему обсуждению вместе со ссылкой на документ msdn на разных – user1376713

ответ

1

1) После добавления этих методов к вашему customPoint

public override int GetHashCode() 
{ 
    return X.GetHashCode() * 19 + Y.GetHashCode(); 
} 

public override bool Equals(object obj) 
{ 
    var other = obj as customPoint; 
    return this.X == other.X && this.Y == other.Y; 
} 

Вы можете использовать Distinct метод Linq в.

var distinctPoints = myPoints.Distinct().ToList(); 

2) Вы можете использовать Анонимных сравнения типа трюка без переопределения любого метода.

var distinctPoints = myPoints.GroupBy(m => new { m.X, m.Y }) 
          .Select(x => x.First()) 
          .ToList(); 

3) Вы также можете сделать это, написав пользовательские IEqualityComparer

public class MyEqualityComparer : IEqualityComparer<customPoint> 
{ 
    public bool Equals(customPoint a, customPoint b) 
    { 
     return a.X == b.X && a.Y == b.Y; 
    } 

    public int GetHashCode(customPoint other) 
    { 
     return other.X.GetHashCode() * 19 + other.Y.GetHashCode(); 
    } 
} 

var distinctPoints = myPoints.Distinct(new MyEqualityComparer()).ToList(); 
+0

Я сделал Equals (но используя IEquatable), но не хэш-код. Я также посмотрел на GroupBy, но имел немного проблем, понимая синтаксис других вопросов, которые использовали его аналогичным образом. Я немного новичок в хэш-кодах, поэтому мне любопытно, почему X умножается на 19 в этом случае? – Xantham

+1

@ Xantham это случайное простое число, оно даже работает, если вы написали 'return 0;'. Хороший алгоритм хэширования может улучшить производительность, это все. Все, что вам нужно, это убедиться, что если два объекта равны, то их хэш-коды должны быть одинаковыми. (Но не обязательно для другого пути) – I4V

+1

А, это имеет смысл. Спасибо. – Xantham

1

Что вы пытались использовать LINQ, который не работал? Приведенный ниже код должен это сделать:

var uniquePoints = myPoints.Distinct(); 
+1

Я подозреваю, что OP называется 'myPoints.Distinct()' и игнорирует возвращаемое значение. –

+0

Ahh, распространенная ошибка. –

+0

Или что он, конечно, не переопределил Equals/GetHashCode :) –

0

метод Distinct бы хороший путь, но для того, чтобы использовать его, как вы хотите, вы должны либо реализовать Equals и GetHashCode на вашем объекте, или создать a IEqualityComparer<customPoint> и передать это в метод Distinct. Для вашего случая, вероятно, было бы целесообразно реализовать эти методы на вашем объекте. Из документации:

Comparer равенство по умолчанию, Default, используется для сравнения значений типов, которые реализуют общий интерфейс IEquatable<T>. К сравнить пользовательский тип данных, вам необходимо реализовать этот интерфейс, и предоставить свои собственные методы GetHashCode и Equals.

0

Я сделал это в LINQPad, поэтому простите Dump() ... но вот способ реализации ваш customPoint класс:

void Main() 
{ 
    var myPoints = new List<customPoint>(); 
    myPoints.Add(new customPoint(1,5)); 
    myPoints.Add(new customPoint(1,5)); 
    myPoints.Add(new customPoint(2,3)); 
    myPoints.Add(new customPoint(4,9)); 
    myPoints.Add(new customPoint(8,7)); 
    myPoints.Add(new customPoint(2,3)); 

    myPoints.Distinct().Dump(); 
} 


public class customPoint { 
    public int X; 
    public int Y; 

    public customPoint(int x, int y){ 
     X = x; 
     Y = y; 
    } 

    public override Boolean Equals(Object rhs) { 
     var theObj = rhs as customPoint; 

     if(theObj==null) { 
      return false; 
     } else { 
      return theObj.X == this.X && theObj.Y == this.Y; 
     } 
    } 

    public override int GetHashCode() { 
     return X^Y; 
    } 
} 
Смежные вопросы