Doppelte Listeneinträge entfernen
Gustl
- perl
undef %saw; @out = grep(!$saw{$_}++, @in);
hab ich im archiv gefunden. weiss nicht ob ich das überhaupt verstehen will :) aber ich brauchs.
wie sag ichs in perl wenn mein $_ aus mehreren positionen besteht und ich davon nur das dritte element brauche ? also so:
$in[0] "frosch;gruen;333;quak;sommer"
$in[1] "kroete;grau;333;quaak;herbst"
$in[2] "amsel;grau;533;sing;abend"
$in[3] "spatz;grau;433;sing;morgen"
$in[4] "katze;weiss;433;miau;immer"
$in[5] "katze;schwarz;533;frisst;maus"
$in[6] "frau;schwarz;333;ist;schoen"
$in[7] "frau;weiss;433;ist;alt"
...
@out muss dann heissen:
$out[0] "333"
$out[1] "533"
$out[2] "433"
...
--
Theorie: Alles ist möglich - Praxis: Nix funzt - Ich bin eben ein Mann der Praxis
hi,
mit split kannst du das zum beispiel machen:
(undef,undef,$ele3,undef) = split(";",$in[0]);
das ganze dann in einer Schleife für jedes Element durchlaufen lassen und das $ele3 jedesmal in eine Liste oder in einen Hash speichern.
Beim Hash kannst so gleich aussortieren, bei ner Liste musst danach nochmal die doppelten Einträge löschen.
mfG,
steckl
Hell-O!
wie sag ichs in perl wenn mein $_ aus mehreren positionen besteht und ich davon nur das dritte element brauche?
Vorschlag: Kombiniere map und grep:
my @in = ('1;foo', '2;bar', '3;baz', '4;foo', '0;baz;2');
my %saw;
my @out = grep(!$saw{$_}++, map((split(';', $_))[1], @in));
Lediglich die Indexnummer deines split musst du noch an deine Bedürfnisse anpassen.
Siechfred
Vorschlag: Kombiniere map und grep:
my @in = ('1;foo', '2;bar', '3;baz', '4;foo', '0;baz;2');
my %saw;
my @out = grep(!$saw{$}++, map((split(';', $))[1], @in));
>
> Lediglich die Indexnummer deines split musst du noch an deine Bedürfnisse anpassen.
>
> Siechfred
ok, map ersetzt schleife uns splittet @in in jeweils [1]. das (! ausrufezeichen) versteh ich nicht, heisst das wenn nicht ? grep extrahiert wert und füllt nach %saw den wert als name ? ich hab da irgendwo eine verständnislücke :(
gustl
Hell-O!
my @in = ('1;foo', '2;bar', '3;baz', '4;foo', '0;baz;2');
my %saw;
my @out = grep(!$saw{$}++, map((split(';', $))[1], @in));
> ok, map ersetzt schleife uns splittet @in in jeweils [1].
Die map-Funktion geht alle Arrayelemente durch und wendet auf sie den Ausdruck an, im vorliegenden Falle split, also in der Tat wie eine Schleife. Die Klammer um split ermöglicht es dir, den gesuchten Teil (das Element mit der Indexnummer 1) direkt auszulesen. Das Ergebnis ist ein namenloses Array der gesuchten Teilelemente, auf welches dann grep angewendet wird.
> das (! ausrufezeichen) versteh ich nicht, heisst das wenn nicht ? grep extrahiert wert und füllt nach %saw den wert als name ?
Die Anweisung liest sich wie folgt:
"Greife dir alle Elemente des namenlosen Arrays, für die `$saw{$_}++`{:.language-Perl} nicht wahr ist"
`!$saw{$_}++`{:.language-Perl} besteht aus zwei Teilen: Dem Hochzählversuch ('++') und dem Vergleichsoperator '!' (der nichts anders bedeutet als "wenn nicht"). Wird versucht, ein nicht existentes Element `$saw{$_}`{:.language-Perl} hochzuzählen, liefert die Operation "falsch", da das Element nicht existiert. Gleichzeitig wird es angelegt (Stichwort Autovivikation). Wird dagegen ein existentes Element hochgezählt, ist das Ergebnis der Operation "wahr".
Jetzt kommt das '!' ins Spiel, das seinerseits die Fälle abfragt, in denen das Hochzählen nicht funktioniert hat, denn nur dann taucht das Element zum ersten Mal auf und muss logischerweise in dein Ergebnisarray aufgenommen werden. Eine doppelte Verneinung sozusagen. Ausführlich könnte man es so veranschaulichen:
~~~perl
$bool = $saw{$_}++;
if($bool) {
print "Schlüssel $_ existiert, sein Wert wurde um 1 erhöht";
}
else {
print "Schlüssel $_ existierte noch nicht und wurde mit dem Wert 1 angelegt";
}
ich hab da irgendwo eine verständnislücke :(
Ich hoffe, ich konnte sie schließen :)
Siechfred
Ich hoffe, ich konnte sie schließen :)
Siechfred
ja die sonne steht schon am nebelrand :) wahrlich eine mächtige zeile code.
danke siechfred für die ausführliche erklärung, das hilft !
grüsse gustl
my @in = ('1;foo', '2;bar', '3;baz', '4;foo', '0;baz;2');
my %saw;
my @out = grep(!$saw{$}++, map((split(';', $))[1], @in));
> > ok, map ersetzt schleife uns splittet @in in jeweils [1].
>
> Die map-Funktion geht alle Arrayelemente durch und wendet auf sie den Ausdruck an, im vorliegenden Falle split, also in der Tat wie eine Schleife. Die Klammer um split ermöglicht es dir, den gesuchten Teil (das Element mit der Indexnummer 1) direkt auszulesen. Das Ergebnis ist ein namenloses Array der gesuchten Teilelemente, auf welches dann grep angewendet wird.
Wobei das map nicht wirklich notwendig ist, im grep kannst du ebenfalls den split Ausdruck anwenden, das dürfte um einiges fixer gehen.
Ansonsten schöne Erklärung ;-)
Struppi.
--
[Javascript ist toll](http://javascript.jstruebig.de/) (Perl auch!)
undef %saw; @out = grep(!$saw{$_}++, @in);
hab ich im archiv gefunden. weiss nicht ob ich das überhaupt verstehen will :) aber ich brauchs.
wie sag ichs in perl wenn mein $_ aus mehreren positionen besteht und ich davon nur das dritte element brauche ? also so:
$in[0] "frosch;gruen;333;quak;sommer"
$in[1] "kroete;grau;333;quaak;herbst"
$in[2] "amsel;grau;533;sing;abend"
$in[3] "spatz;grau;433;sing;morgen"
$in[4] "katze;weiss;433;miau;immer"
$in[5] "katze;schwarz;533;frisst;maus"
$in[6] "frau;schwarz;333;ist;schoen"
$in[7] "frau;weiss;433;ist;alt"
...@out muss dann heissen:
$out[0] "333"
$out[1] "533"
$out[2] "433"
z.b. auch so:
use strict;
my @in = (
"frosch;gruen;333;quak;sommer",
"kroete;grau;333;quaak;herbst",
"amsel;grau;533;sing;abend",
"spatz;grau;433;sing;morgen",
"katze;weiss;433;miau;immer",
"katze;schwarz;533;frisst;maus",
"frau;schwarz;333;ist;schoen",
"frau;weiss;433;ist;alt"
);
my %saw;
grep !$saw{( split/;/,$_)[2]}++ , @in;
my @out = keys %saw;
print join "\n", @out;
Struppi.
my %saw;
grep !$saw{( split/;/,$_)[2]}++ , @in;
my @out = keys %saw;
>
> Struppi.
also von innen nach aussen: splittet @in in der schleife ! .. ++ und füllt %saw name durch grep mit dem element 2 von $\_ !? ähm oder so ...
keys ermittelt alle namen von hash, sortiert also doppelte gleich aus ? was steht in wert von hash ? nix ?
ich denke ich habs noch nicht wirklich gefressen.
gustl
my %saw;
grep !$saw{( split/;/,$_)[2]}++ , @in;
my @out = keys %saw;
> >
> > Struppi.
>
> also von innen nach aussen: splittet @in in der schleife ! .. ++ und füllt %saw name durch grep mit dem element 2 von $\_ !? ähm oder so ...
Ehrlich gesagt ist grap und map mir teilweise ebenfalls ein bisschen suspekt ;-)
> keys ermittelt alle namen von hash, sortiert also doppelte gleich aus ? was steht in wert von hash ? nix ?
In %saw sollte die Anzahl der jeweiligen Werte stehen (mit Dumper \%saw kannst du es kontrollieren - use Data::Dumper nicht vergessen)
Struppi.
--
[Javascript ist toll](http://javascript.jstruebig.de/) (Perl auch!)
hallo,
Ehrlich gesagt ist grap und map mir teilweise ebenfalls ein bisschen suspekt ;-)
hat die Verwendung von grep und map irgendwelche Vorteile gegenueber einer Schleife, in der du dann alles Schritt fuer Schritt machst?
Fuer mich ist es eher so, dass ich die Schreibarbeit die ich mir durch solche (fuer mich) abstrakten Konstruke sparen wuerde hinterher wieder mehr habe, um die entsprechenden Codezeilen ausfuehrlich zu kommentieren, weil ich sonst nichtmehr durchblicken wuerde was da passiert.
Kann aber auch sein, dass das so ist, weil ich mich noch nicht lange genug mit Perl befasse.
mfG,
steckl
Fuer mich ist es eher so, dass ich die Schreibarbeit die ich mir durch solche (fuer mich) abstrakten Konstruke sparen wuerde hinterher wieder mehr habe, um die entsprechenden Codezeilen ausfuehrlich zu kommentieren, weil ich sonst nichtmehr durchblicken wuerde was da passiert.
Kann aber auch sein, dass das so ist, weil ich mich noch nicht lange genug mit Perl befasse.
mfG,
steckl
geht mir ähnlich und step by step kann ichs auch besser kapieren da ich nicht wirklich kryptisch denken kann. nur ist es so dass a) die scripte immens aufgebläht werden b) man durch diese konstrukte viel lernt und c) ists der programmierer-stolz der riesige aufgaben mit nur 9,5 zeichen erledigen will :) so meine meinung dazu.
kommentar gehört nich hierher ich kanns aber nicht lassen ...
grüsse gustl
hat die Verwendung von grep und map irgendwelche Vorteile gegenueber einer Schleife, in der du dann alles Schritt fuer Schritt machst?
sie ist vermutlich schneller...
ich hab's mal getestet, bei wenig Einträgen ist die Version mit grep fast doppelt so schnell. Je größer das Array umso geringer wird der Unterschied.
Fuer mich ist es eher so, dass ich die Schreibarbeit die ich mir durch solche (fuer mich) abstrakten Konstruke sparen wuerde hinterher wieder mehr habe, um die entsprechenden Codezeilen ausfuehrlich zu kommentieren, weil ich sonst nichtmehr durchblicken wuerde was da passiert.
Bei allem Verständnis dafür, aber ich erkenne keine bessere Lesbarkeit zwischen den beiden Versionen:
my %saw;
grep $saw{(split/;/,$_)[2]}++ , @in;
my @out = keys %saw;
my %saw;
foreach(@in)
{
$saw{(split/;/,$_)[2]}++;
}
my @out = keys %saw;
grep und map sind mächtige Befehle, sie durchlaufen einfach Listen und machen etwas mit dem Inhalt. Insofern finde ich sie nicht sonderlich abstrakt, was ich mir nicht merken kann ist, was sie konkret zurückgeben.
Struppi.
Bei allem Verständnis dafür, aber ich erkenne keine bessere Lesbarkeit zwischen den beiden Versionen:
Struppi.
--
ich denke was steckl meinte ist zwischen einer herkömmlichen schleifenkonstruktion und diesen konstrukten.
ich mach mich wieder ran. winke gustl
Bei allem Verständnis dafür, aber ich erkenne keine bessere Lesbarkeit zwischen den beiden Versionen:
Struppi.
--
ich denke was steckl meinte ist zwischen einer herkömmlichen schleifenkonstruktion und diesen konstrukten.
Was heißt herkömmliche Schleifenkontrukte?
Das eine Beispiel von mir war doch ein Schleifenkontrukt?
Struppi.
hi,
Was heißt herkömmliche Schleifenkontrukte?
Das eine Beispiel von mir war doch ein Schleifenkontrukt?
ich hätts wohl so irgendwie gemacht:
my %saw;
foreach my $line (@in)
{
my $ele3 = (split(';',$line))[2];
$saw{$ele3} = 1;
}
my @out = keys %saw;
Das würde ich persönlich bevorzugen, auch wenns wohl unnötig lang ist.
mfG,
steckl
Was heißt herkömmliche Schleifenkontrukte?
Das eine Beispiel von mir war doch ein Schleifenkontrukt?Struppi.
SORRY - ja du hast recht ich habs zu flüchtig angeschaut :) schäm
Gustl
In %saw sollte die Anzahl der jeweiligen Werte stehen (mit Dumper %saw kannst du es kontrollieren - use Data::Dumper nicht vergessen)
Struppi.
ja es stimmt. in wert steht die anzahl der ehemaligen vorkommen, in name die von doppelten bereinigten vorkommen. dann muss man mit key nur noch die namen ermitteln. schon irgendwie genial, smile.
kann ich jetzt so verarbeiten, super ! für was das (!) steht hab ich aber immer noch nicht wirklich geschnallt. kann man das mit "if not" übersetzen ?
danke - bis dahin Struppi
Gustl
Hallo Struppi,
my %saw;
grep !$saw{( split/;/,$_)[2]}++ , @in;
my @out = keys %saw;
print join "\n", @out;
Anschaulicher finde ich diese (ähnliche) Möglichkeit:
~~~perl
sub unique {
my %saw;
map { $saw{(split /;/)[2]} = 0 } @_;
return( keys %saw );
}
Eine Aufzählung wie oft ein Element vorkommt ist nicht notwendig. Außerdem ist die Unterscheidung mit grep zw. im Hash bereits gesetzen und noch nicht gesetzen Werten überflüssig.
Jasmin