Ich hab mir da mal eine Funktion geschrieben, die aus einem Array schnell alle doppelten Werte herausfiltert.
Schnell bedeutet übrigens: Ich habe verschiedene Möglichkeiten auf Ihre Schnelligkeit hin getestet und diese war die Schnellste...
Code:
sub del_double{ #Parameter: @liste, die aussortiert werden soll my %all; grep {$all{$_}=0} @_;
return (keys %all);
}
Aufgerufen wird das Ganze mit
@array=&del_double(@array);
Und wie funktioniert das nun?
Übergeben wird das gewünschte Array. Danach wird über den grep-Befehl jeder Wert durchgegangen und einem temporären Hash als Key zugewiesen. Das Value ist übrigens in diesem Zusammenhang egal.
Wen alle Werte durch sind, werden alle Keys des Hashes als neues Array zurückgegeben. Fertig!
Vorsicht
Die Reihenfolge des Arrays wird verändert!
Für schnellere Methoden bin ich übrigens dankbar!
Aktualisierung
Ein Leser schrieb unten eine schnellere Methode, hier der Code:
sub del_double
{ my %all;
$all{$_}=0 for @_;
return (keys %all);
}
Dieser Code ist fast 50 % schneller als mein bisheriger Spitzenreiter.
Danke nochmals!
Natürlich bin ich aber immer noch dankbar für noch schnellere Methoden.
timethis (5, sub{@b=del_double(@a)}); # original-Code
timethis (5, sub{$all{$_}=0 for @a; @b=(keys %all);}); # hier neuer Code, der verglichen werden soll
Nachtrag, die 2.
Also, da ich gerade dieses Beispiel prädestiniert dafür halte, über die Vielfältigkeit der Lösungsmöglichkeiten in Perl zu schreiben, habe ich mich nun etwas in die weiten des Webs begeben und nach den schnellsten Lösungsmöglichkeiten gesucht.
Die ultimativ schnellste Möglichkeit scheint folgende zu sein:
sub del_double{ my %all=();
@all{@_}=1;
return (keys %all);
}
Diese Methode verzichtet ganz auf irgendwelche grep's, for's oder foreach's, sondern weist dem Hash %all direkt die keys zu, die aus dem übergebenen Array stammen.
Der Geschwindigkeitsvorteil ist gravierend:
Meine Originalmethode: 4.42 x pro Sekunde
Die bessere Methode von gestern: 5.9 x pro Sekunde
Die ganz neue Methode: 8 x pro Sekunde
Also ist die neueste Methode fast doppelt so schnell als meine erste... naja, auf die Idee muß man aber auch erst mal kommen.
Und wie geht es nun?
Nunja, das ist schnell beschrieben:
Das übergebene Array in @_ füllt den Hash mit den keys aus @_ und den Values 1(die völlig egal sind, man hätte auch sonstwas nehmen können).
Das das funktioniert war mir neu, aber man sollte sich bei Perl glaube ich eh' abgewöhnen, sich zu wundern...
Gefunden hab ich die neue Methode übrigens auf Perlmonks.org, einer wahren Fundgrube über Perl.
Kommentare zum Beitrag "Doppelte Einträge aus Array entfernen in Perl"
Kommentar von Harald Mattern
Eine schnellere Methode
sub del_double { my %seen; return grep{ not $seen{$_}++ } @_; }
Kommentar von Perl-Hasser
igitt
Kommentar von ceyhant
sub del_double{
return keys %{{ map { $_ => 1 } @_ }};
}
Kommentar von Admin
Sorry, aber ein Test mit Benchmark und folgenden Code bei 1.000.000 Array-Werten
ergibt, daß Deine Methode fast halb so langsam ist wie meine Originalmethode...
Kommentar von Mr. T
die Geschichte scheint etwas schneller zu sein
sub del_double2
{
my %all;
$all{$_}=0 for @_;
return (keys %all);
}
Kommentar von Admin
Bingo!
Diese Variante ist tatsächlich schneller.
Mit meiner Variante läuft ein durchlauf mit 1 Mio zufälligen Werten laut Benchmark 1.79 mal pro Sekunde, mit der neuen Variante 2.56 mal pro Sekunde. Dies ist ein Geschwindigkeitsvorteil von fast 50%.
Bravo und dankeschön!
Kommentar von Basti
Hallo ich habe mal eine Frage zu euren Vorschlägen.
Mein Problem ist, dass ich knapp 1300 Zeilen mit je 2 Spalten Koordinatenzahlen habe und nun die doppelten aussortieren möchte. Mit den Lösungen in diesem Forum geht das auch soweit wirklich prima, jedoch darf sich die Reihenfolge nicht verändern....
Hat hierzu jemand vielleicht einen Vorschlag? Ich wäre euch sehr dankbar, habe erst vor zwei Wochen mit Perl angefangen und aus dem Studium nur geringe C++ Kenntnisse... Bin also was das Programmieren betrifft noch ungeübt. Mein Programm bis dahin steht zwar, aber das bereitet mir jetzt Probleme.
Danke Grüße
Kommentar von Admin
Das Problem ist etwas mißverständlich beschrieben...
Hast Du Daten in Form
52.22N 10.47E
34.33N 11.34E
17.24N 59.32E
52.22N 10.47E
11.45N 34.37E
?
Wenn ja, würde folgendes helfen:
my %seen; my @unique = grep{ ! $seen{$_}++ }@array;
Alle doppelten Elemente, die in der ersten UND zweiten Spalte übereinstimmen, werden ausgefiltert.
Hast Du Daten in der obigen Form und es darf das Erste ODER das Zweite nicht doppelt sein, hilft folgendes weiter:
die doppelten sind also mehr oder weniger zufällig verteilt. Jetzt soll die erste der doppelten oder gar drei bis vierfach vorhandenen Zeilen erhalten bleiben.
Wichtig ist ausserdem, dass die Reihenfolge der gesamten Liste ebenfalls identisch bleibt.
Ich hoffe, dass es nun besser verständlich ist. Danke nochmal :)
Kommentar von basti
oh jetzt hab ich verstanden was sie meinten... :)
Ja es müssen erste UND zweite Spalte einer Zeile mit der ersten UND zweiten Spalte einer anderen Zeile übereinstimmen, damit der "lösch-Befehl" erteilt werden soll.
Ich probier das erste gleich mal aus.
gruß
Kommentar von Struppi
Das ist übrigesn eine Perl FAQ (steht also in jeder Perl Doku)
http://faq.perl.org/perlfaq4.html# How_can_I_remove_dup
und die schnelste Möglichkeit heißt Hash-Slice
Man muss schon echter Perl Hasser sein um sowas nicht zu mögen ;-)
Kommentar von Basti
also vielen Dank noch einmal, hat alles prima geklappt und ich hab nun meine Koordinatenliste.
Jetzt habe ich aber ein neues kleines Problem, an dem ich irgendwie im Moment hänge:
ich habe jetzt eine .txt Datei, mit folgendem Aufbau:
ich will nun die Reihenfolge ändern und zwar zuerst nach der Ykoordinate in aufsteigender Richtung und dann nach der Xkoordinate, ebenfalls in aufsteigender Richtung Sortiert.
dabei soll das Ergebnis der Drei Spalten wieder in die .txt Datei geschrieben werden. (am besten die .txt Datei überschreiben)
hat wer eine Lösung hierfür? Stehe da momentan aufm Schlauch... oder es ist einfach schon zu Spät heut.
Ich danke euch schon einmal und wünsche ein schönes Wochenende :)
Wie gewünscht nach y, dann x, z wird nicht beachtet.
Wie das funktioniert kannst Du später hier im Blog Schwartzsche Transformation lesen.
Oder hier, wo die Schwartzsche Transformation schonmal angesprochen wurde
Kommentar von Basti
Oh man ihr seit echt klasse!
Danke! Das hilft mir sehr weiter. Mit dem wie und warum werd ich mich später auf jedenfall beschäftigen, jetzt gilts erstmal meine Arbeit abzugeben :)
Kommentar von Basti
Hallo nochmal.
Ich habe mein Programm entsprechend angepasst und nun kommt folgende Fehlermeldung:
Can't use an undefined value as an ARRAY reference at ...
die Zeile des Fehlers:
my @sortiert = ( map { $_="$_->[0]\; $_->[1]\; $_->[2]" }
Ich weiss jetzt nicht, ob das ganze zu "herausgerissen" aus meinem gesamten Programm ist und deshalb nicht zu beantworten ist, aber ich habe mir jetzt fast 3Stunden alles genau angeschaut und durchgelesen und komme nicht weiter.
Grüße
Kommentar von Admin
Hi Basti,
der Code den ich oven gepostet habe war wohl ein bißchen buggi, weiss auch nicht wo ich den mal verwendet habe...
Hier der neue Code, der sollte auf jeden Fall laufen