2012-04-01 22 views
2

Ich habe eine Liste von Objekten, die geordnet sind. Ich möchte redundante Objekte entfernen, bei denen Redundanz nicht unbedingt Duplikat bedeutet. Hier ein Beispiel:Gibt es eine Möglichkeit, bestimmte Elemente aus einer Liste <T> mit LINQ zu entfernen?

List<Point> points = new List<Point> 
{ 
    new Point(0, 10), 
    new Point(1, 12), 
    new Point(2, 16), 
    new Point(3, 16), 
    new Point(4, 16), 
    new Point(5, 13), 
    new Point(6, 16), 
}; 

Ich interessiere mich für das Entfernen der new Point(3, 16) Eintrag, weil es nicht sinnvoll, Informationen liefert; Ich weiß bereits, dass das Element bei 2 = 16 und das Element bei 4 = 16 ist. Die Information, dass 3 = 16 mir in meiner Anwendung nicht gut tut (weil ich bereits die Grenzen {2,4} = 16) habe, möchte ich diesen Eintrag entfernen. Ich möchte auch nicht, dass ich den 5. und 6. Eintrag behalten möchte, da es keine aufeinanderfolgenden Einträge gibt, bei denen Y = 16 ist.

Gibt es einen glatten Weg, dies mit Linq zu tun?

+1

How do you "redundant" Daten bestimmen? Ist dies in Form einer Funktion der Form f = y (x)? Sie scheinen willkürlich einen Datenpunkt zu entfernen. Wenn Ihre Funktion stückweise linear zwischen zwei Punkten 'a, b' und' f (a) = f (b) 'ist, können Sie sie entfernen. Aber Sie scheinen keine Rechtfertigung dafür zu geben, dies zu beseitigen. –

+1

Ja. Wenn ich mehrere aufeinanderfolgende Punkte habe, die eine stückweise horizontale Linie bestimmen, möchte ich die inneren Punkte entfernen und die Endpunkte einfach belassen. – Mark

+0

@MikeBantegui - Ich denke, die Probe + Text Erklärung sind klar genug. Obwohl einige Erläuterungen zu vertikalen und vor allem diagonalen Segmenten hilfreich wären. –

Antwort

1

Was ist das?

public void Test() 
{ 
List<Point> points = new List<Point> 
{ 
    new Point(0, 10), 
    new Point(1, 12), 
    new Point(2, 16), 
    new Point(3, 16), 
    new Point(4, 16), 
    new Point(5, 13), 
    new Point(6, 16), 
}; 
var subSetOfPoints = points.Where(p=> !IsInTheMiddleX(p, points)); 
} 

private bool IsInTheMiddleX(Point point, IEnumerable<Point> points) 
{ 
return points.Any(p => p.X < point.X && p.Y == point.Y) && 
     points.Any(p => p.X > point.X && p.Y == point.Y);       
} 
+0

Funktioniert gut und besser als meine Antwort .. –

+0

@HaLaBi Sie sollten beachten, dass dies nicht wirklich entfernt Punkte aus der Liste. Stattdessen wird ein Enumerator zurückgegeben, der diese Punkte überspringt. Für viele Zwecke ist das in Ordnung, aber Sie müssen bedenken, dass sich die Variable "Punkte" weiterhin auf die vollständige Liste der Punkte bezieht. Am Ende der 'Test()' Methode enthält es immer noch sieben Elemente. – phoog

+1

Was ist mit Punkten in verschiedenen Winkeln?Dies berücksichtigt nur Punkte, die entlang der x-Achse liegen. –

0

Ich würde Ihre eigene Klasse definieren, die eine List<Point> umschließt, so dass Sie Ihre eigene Logik implementieren können und definieren, was "redundant" oder andere Regeln ist, die Sie einhalten möchten. Es scheint, dass dies vernünftiger wäre als die Verwendung von LINQ.

1

bearbeiten: Dies gibt Ihnen das erwartete Ergebnis. Ich bin eine Gruppierung List<Point> (geordnet nach Point.X) von Point.Y. Dann nehme ich die erste und letzte Point in jeder Gruppe:

var result = points.OrderBy(p => p.X) 
      .GroupBy(p => p.Y) 
      .Select(grp => 
       grp.Where((p, index) => index == 0 || index == grp.Count() - 1)) 
       .SelectMany(p => p).OrderBy(p => p.X) 
      .ToList(); 
+0

Und jetzt der allgemeinere 'y = ax + b' Fall. Nur zum Spaß. –

+0

falsches Ergebnis ... –

+0

@HenkHolterman: Ich bin mir nicht sicher, ob das überhaupt Sinn macht, da das Zeichnen mit LINQ keine meiner Stärken ist;) Ich habe meine Antwort zur Vereinfachung/Beschleunigung bearbeitet, mittlerweile gab es eine Version, die nicht funktionierte, aber das sollte. –

0

Hier ist mein Versuch, es funktioniert gut, aber der Code muss besser sein:

var distinctY = points.Select(p => p.Y).Distinct().ToList(); 

List<Point> filtered = new List<Point>(); 
foreach (var y in distinctY) 
{ 
    int minX, maxX; 

    minX = points.Where(p => p.Y == y).Min(x => x.X); 
    maxX = points.Where(p => p.Y == y).Max(x => x.X); 

    filtered.Add(new Point(minX, y)); 

    if (maxX != minX)  
     filtered.Add(new Point(maxX, y)); 
} 
0

Wie der Name der Daten zur Abfrage LINQ zeigt, sollte es nur verwendet werden.

Wenn ich Sie wäre, würde ich nicht LINQ verwenden, um die Liste zu löschen oder zu ändern. Ich benutze es nur, um es abzufragen.

Verwandte Themen