• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

Mit AWK verschiedene Felder verschiedener Zeilen vergleichen

Hallo alle zusammen!

Bei folgender Aufgabe komme ich nicht weiter.
Hier ist der zu verarbeitende Inhalt einer Datei (die Zeilennummern gehören nicht zum Inhalt, nur für die nachfolgende Erläuterung der Aufgabenstellung):

01. AAA;AAB;10
02. AAB;AAA;12
03. AAA;FAC;26
04. AAA;FAV;15
05. DAD;HHZ;89
06. AAA;HOI;33
07. AAA;MKP;59
08. AAA;PPT;45
09. AAA;RRR;88
10. AAA;TTQ;87
11. HHZ;DAD;89
12. MKP;AAA;59

Diese Datei muss eingelesen werden, ausgegeben müssen alle vorhandenen Zeilen und die Zeilen, wo $1 der Zeile n nicht gleich $2 der Zeile m ist und $2 der Zeile n nicht gleich $1 der Zeile m ist, und in dieser Reihenfolge: $2;$1;$3.

Also, Zeile 1 und Zeile 2 müssen nicht angefasst werden, weil da findet man in beiden Zeilen Werte AAA und AAB, nur verdreht. Genauso Zeile 5 und Zeile 11, und Zeile 7 und Zeile 12.
Alle anderen Zeilen haben sozusagen, keine gespiegelten "Doppelgänger" und für Sie muss so ein "Doppelgänger" generiert und der Datei hinzugefügt werden (an welcher Stelle, spielt keine Rolle).

Das ist der gewünschte Output:

01. AAA;AAB;10
02. AAB;AAA;12
03. AAA;FAC;26
04. AAA;FAV;15
05. DAD;HHZ;89
06. AAA;HOI;33
07. AAA;MKP;59
08. AAA;PPT;45
09. AAA;RRR;88
10. AAA;TTQ;87
11. HHZ;DAD;89
12. MKP;AAA;59
13. FAC;AAA;26
14. FAV;AAA;15
15. HOI;AAA;33
16. PPT;AAA;45
17. RRR;AAA;88
18. TTQ;AAA;87

Könnte mir bitte jemand helfen? Alle meine Versuche bleiben bisher erfolglos. Muss auch nicht unbedingt mit AWK erledigt werden. Danke!
 
A

Anonymous

Gast
Code:
awk  ' BEGIN{
        OFS=";"
        FS=";"
        };
        {x[$1,$2]=$3};
        END{for (i in x) {
                split(i,y,SUBSEP);
                print y[1],y[2],x[i]
                if (!((y[2],y[1]) in x)) {
                        print y[2],y[1],x[i]
                }
        };
}'    deine_testdatei

Aber frag nicht wie das funktioniert ;)
arbeiten mit mehrdimensionale Arrays in AWK sind nicht das einfachste und um das zu verstehen sollte man schon zumindest ein bisschen in AWK Dokus geblättert haben.

Nun gehe er und verkünde das freudig Ergebnis in all den Foren seines von Frevel geprägten Multiposts um weitere Versuche in dafür unbrauchbaren Sprachen frühzeitig zu beenden noch bevor die festlichen Tage ausgelaufen sind.

robi
 

framp

Moderator
Teammitglied
Vielleicht interessiert es Dich ja wie ein Algorithmus in go aussehen könnte. Wie man schnell sieht geht es aber nicht so kurz und knapp wie in Robis awk Lösung :-/
Code:
package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

const separator = ";"

// AAA;AAB;10
type key struct { // key into map of elements found in file
	key1 string // AAA
	key2 string // AAB
}

func (k key) String() string { // Stringer for key to print string representation of key
	return fmt.Sprintf("%s%s%s", k.key1, separator, k.key2)
}

func main() {

	elements := make(map[key]string) // value in map is last element 10
	n := 1                           // just a simple counter for all elements

	file, _ := os.Open("pair.dat") // read input data from file
	defer file.Close()             // make sure it's closed at end of prog

	scanner := bufio.NewScanner(file)
	for scanner.Scan() { // read file line by line
		    token := strings.Split(scanner.Text(), separator) // split line into 3 tokens separated by ;
		    k := key{token[0], token[1]}                      // build key AAA;AAB
		    elements[k] = token[2]                            // store value for key in map
		    fmt.Printf("%02d: %s;%s\n", n, k, token[2])       // just print the line read
		    n++
	}

	for e, _ := range elements { // loop over all elements
  		    reverseKey := key{e.key2, e.key1}       // build reverse key
		    if _, ok := elements[reverseKey]; !ok { // if reverse key does NOT exist
			        fmt.Printf("%02d: %s;%s\n", n, reverseKey, elements[e]) // print reverse key with value from key
			        n++
		    }
	}
}

/* Result of run:

01: AAA;AAB;10
02: AAB;AAA;12
03: AAA;FAC;26
04: AAA;FAV;15
05: DAD;HHZ;89
06: AAA;HOI;33
07: AAA;MKP;59
08: AAA;PPT;45
09: AAA;RRR;88
10: AAA;TTQ;87
11: HHZ;DAD;89
12: MKP;AAA;59
13: RRR;AAA;88
14: FAC;AAA;26
15: HOI;AAA;33
16: PPT;AAA;45
17: TTQ;AAA;87
18: FAV;AAA;15

*/
 
OP
A

andreasandy

Newbie
Hi Robi, vielen Dank für Deine Hilfe! Das funktioniert.
Ich werde mich mit dem Thema "mehrdimensionale Arrays" aufs Neue auseinander setzen.

An tomm.fa: danke für den Hinweis, ich habe das zur Kenntnis genommen.
 
OP
A

andreasandy

Newbie
Hallo framp, ich danke Dir! Ich bin für alle Anregungen dankbar, da ich mich auf dem Lernpfad befinde.
Das ist interessant.
 

framp

Moderator
Teammitglied
Und was ist mit meinem Vorschlag in go ? Schmoll :(

... just kidding ;) - go ist schon exotisch - hatte nur Lust das mal auszuprobieren
 

abgdf

Guru
Perl mit Klassen:
Code:
#!/usr/bin/perl

use warnings;
use strict;

package Line {

    sub new {
        my $classname = shift;
        my $self = {nr => shift,
                    first => shift,
                    second => shift,
                    third  => shift,
                    output  => 1};
        return bless($self, $classname);
    }

    sub check {
        my $self = shift;
        my $nr_in = shift;
        my $first_in = shift;
        my $second_in = shift;
        if ($self->{nr} eq $nr_in) {
            return;
        }
        if ($self->{first} eq $second_in &&
            $self->{second} eq $first_in) {
            $self->{output} = 0;
        }
    }

    sub checkandprint {
        my $self = shift;
        my $count = shift;
        if ($self->{output} == 1) {
            print "$count. ";
            print $self->{second};
            print ";";
            print $self->{first};
            print ";";
            print $self->{third};
            print "\n";
        }
    }
}
          
my @a = <DATA>;
my @lines = ();
my $i;
my $u;

my $n = 1;
my $count = 0;
foreach $i (@a) {
    chomp($i);
    $count++;
    if ($count < 10) {
        print "0";
    }
    print "$count. $i\n";
    my @b = split(/;/, $i);
    push (@lines, Line->new($n, $b[0], $b[1], $b[2]));
    $n++;
}

foreach $i (@lines) {
    foreach $u (@lines) {
        $i->check($u->{nr}, $u->{first}, $u->{second});
    }
}

foreach $i (@lines) {
    if ($i->{output} == 1) {
        $count++;
    }
    $i->checkandprint($count);
}

__DATA__
AAA;AAB;10
AAB;AAA;12
AAA;FAC;26
AAA;FAV;15
DAD;HHZ;89
AAA;HOI;33
AAA;MKP;59
AAA;PPT;45
AAA;RRR;88
AAA;TTQ;87
HHZ;DAD;89
MKP;AAA;59
 
A

Anonymous

Gast
Nur mal um die alten Kamelen mal wieder aufzuwärmen und zu den Feiertagen mal wieder ein bisschen schmunzeln in das eine oder andere Gesicht zu zaubern.
Sind ja in den verschiedenen Foren die unterschiedlichsten Lösungen eingetroffen. Sollte man sich mal die Zeit nehmen und die einzelnen Autoren versuchen in die Evolution eines Programmierers einzuordenen.

abgdf schrieb:
für den Chefprogrammierer reichst noch nicht ganz ;)

robi
 

abgdf

Guru
robi schrieb:
arbeiten mit mehrdimensionale Arrays in AWK sind nicht das einfachste und um das zu verstehen sollte man schon zumindest ein bisschen in AWK Dokus geblättert haben.
Eben, das ist in Perl auch so. Mit den Objekten komm' ich um die mehrdimensionalen Arrays 'rum (beides, OOP und AoA, ist überhaupt erst mit Perl 5 dazugekommen). Die Attribute in dem Objekt sind dann einfache Skalare.
Wenn man sich erstmal an die Klassendefinition gewöhnt hat, und daran, wie man auf Attribute und Methoden zugreift - was im Prinzip genau wie in Python funktioniert -, finde ich es leichter und übersichtlicher mit Objekten umzugehen als mit komplexen AoA oder HoH-Strukturen.
Schöne Weihnachten noch. :)
 

framp

Moderator
Teammitglied
Netter Link von Dir Robi :thumbs:

Anbei noch eine bash Version :D.
Code:
#!/bin/bash

declare -A map

while IFS=";" read -a e; do				# read elements and store in map
	map["${e[0]};${e[1]}"]=${e[2]}
done < pair.dat

n=1
for key in "${!map[@]}"; do				# process all elements
	e=(${key//;/ })						# split key elements
	reverseKey="${e[1]};${e[0]}"		# build reverse key
	echo "$n: $key;${map[$key]}"
	(( n++ ))
	if ! [[ ${map[$reverseKey]+x} ]]; then	# if reverse key does NOT exists
		echo "$n: $reverseKey;${map[$key]}"
		(( n++ ))
	fi
done
 

}-Tux-{

Hacker
Hier mal ein kleiner perl Ein- bzw. Dreizeiler:
Code:
perl -e 'my %h = map { $_ =~ s/^([^;]+);([^;]+);(.*)\n/$1;$2/r => "$3" } <>; print "$_;$h{$_}\n" for (sort(keys(%h))); print join(";", @$_) . "\n" for grep { !$h{$_->[0]} } map { [s/^([^;]+);([^;]+)$/$2;$1/r, $h{$_}] } sort(keys(%h));' filename

}-Tux-{
 
OP
A

andreasandy

Newbie
framp schrieb:
Sehe gerade dass sich unsere Posts überschnitten haben.

Alles gut :thumbs:

Hallo framp :)

Ich habe nur vor ein Paar Tagen zum ersten Mal etwas über Go gehört. Steht nicht ganz oben auf meiner Prio-Liste, ich werde es mir aber bei Gelegenheit anschauen. Ich verspreche es Dir :)
 
OP
A

andreasandy

Newbie
}-Tux-{ schrieb:
Hier mal ein kleiner perl Ein- bzw. Dreizeiler:
Code:
perl -e 'my %h = map { $_ =~ s/^([^;]+);([^;]+);(.*)\n/$1;$2/r => "$3" } <>; print "$_;$h{$_}\n" for (sort(keys(%h))); print join(";", @$_) . "\n" for grep { !$h{$_->[0]} } map { [s/^([^;]+);([^;]+)$/$2;$1/r, $h{$_}] } sort(keys(%h));' filename

}-Tux-{

Hallo }-Tux-{ ,

Vielen Dank! Ich werde mir das anschauen!
 

abgdf

Guru
Hübsch häßlich isses ja. Immer, wenn "map()" ins Spiel kommt, steig' ich aus, wird mir dann zu unübersichtlich.
 
OP
A

andreasandy

Newbie
Nun hat das Input-File in Wirklichkeit so eine Form:

PVO;20170301
POU;20170531
SAT;AAA;AAB;10
SAT;AAB;AAA;12
SAT;AAA;FAC;26
SAT;AAA;FAV;15
SAT;DAD;HHZ;89
SAT;AAA;HOI;33
SAT;AAA;MKP;59
SAT;AAA;PPT;45
SAT;AAA;RRR;88
SAT;AAA;TTQ;87
SAT;HHZ;DAD;89
SAT;MKP;AAA;59
ODT;508

Das heißt, es müssen noch immer die ersten 2 Zeilen und eine letzte entfernt werden, und bei den nützlichen Zeilen muss das "SAT;" entfernt werden (das "SAT" steht da immer vor diesen Zeilen).

Mit AWK und mit Hilfe von robi habe ich das so gelöst:

Code:
BEGIN{
        OFS=";"
        FS=";"
};

{	if ($1 == "SAT")
{
	x[$2,$3]=$4};
};

END{
	for (i in x) 
	{
           	split(i,y,SUBSEP);
           	print y[1],y[2],x[i]
           	if (!((y[2],y[1]) in x)) 
		{
               		print y[2],y[1],x[i]
		}
       	}	
}

Und das funktioniert. Nur verstehe ich nicht, warum keine abschließende geschweifte Klammer zu diesem Ausdruck erforderlich ist, und warum das so funktioniert:
Code:
{	if ($1 == "SAT")

Wenn ich diese Klammer vor dem "if" entferne, oder noch eine abschließende hinzufüge, funktioniert das nicht mehr.

Außerdem, möchte ich jetzt versuchen das mit dem "SAT" und nicht benötigten Zeilen auch in Python zu lösen.
Aber lässt mich bitte das selbst versuchen :)

Viele Dank Euch und schöne Grüße,
andreasandy
 

framp

Moderator
Teammitglied
andreasandy schrieb:
Ich habe nur vor ein Paar Tagen zum ersten Mal etwas über Go gehört. Steht nicht ganz oben auf meiner Prio-Liste...
Wenn Du an einem Job bei Google interessiert bist solltest Du Deine Prioritäten vielleicht ändern :wink:
 
Oben