Schrift
[thread]6819[/thread]

Verzeichnisbaum: Flaches Array mehrdimensional printen



<< |< 1 2 >| >> 14 Einträge, 2 Seiten
root
 2005-03-26 12:20
#52969 #52969
User since
2003-08-15
120 Artikel
BenutzerIn
[default_avatar]
Hallo!
Für den Betreff stehe ich genauso auf dem Schlauch wie für mein Problem. Mir fällt keine schöne Möglichkeit ein!

Ziel:
Ich möchte mit JavaScript ein Verzeichnisbaum darstellen. Dazu habe ich hier etwas schönes gefunden: http://www.softcomplex.com/products/tigra_tree_menu/docs/#hierarchy

Problembeschreibung:
Meine Dateien sind Datenbankzeilen. Die den Spaltennamen "id", "filename", "content" etc. haben.
Nun muss ich aus den Einträgen von Filename den Javascriptcode für den Baum erstellen.

Praktisch heißt das folgendes:

Hieraus, (Keys von einem Hash)...
Code: (dl )
1
2
3
4
verz1/datei4
test
verz1/verz2/verz3/file3
verz1/file2


...muss das werden(als print, nicht als Struktur in Perl):
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var TREE_ITEMS = [
['Home', null,
['test', "Link mit id aus Datenbank"],
['verz1', null,
['file2', "Link mit id aus Datenbank"],
['verz2', null,

['ver3', "Link mit id aus Datenbank",

['file3', "Link mit id aus Datenbank"],

],
],
],
],
];


Ich komme auf keinen schönen Ansatz!

Danke für eure Hilfe!

Grüße
renee
 2005-03-26 14:23
#52970 #52970
User since
2003-08-04
14371 Artikel
ModeratorIn
[Homepage] [default_avatar]
Vielleicht hilft Dir das als Ansatzpunkt:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#! /usr/bin/perl

use strict;
use warnings;
use Data::Dumper;

my %hash;
while(<DATA>){
next if($_ =~ /^\s*?$/);
$_ =~ s/^\s+//;
my $path = $_;
$path =~ s/^\///; #/
my ($key,$sub) = split(/\//,$path,2);
$hash{$key} = insert_rec($key,$sub,$hash{$key});
}
print Dumper(\%hash);

# insert_rec builds the hash recursivly.
# Parameters:
# key of anonymous hash that has to be expanded
# remaining path
# reference to hash
sub insert_rec{
my ($key2,$path,$hashref) = @_;
my ($key,$sub) = split(/\//,$path,2);
unless($sub){
$hashref->{$key} = {};
return($hashref);
}
$hashref->{$key} = insert_rec($key,$sub,$hashref->{$key}) if($sub);
return ($hashref);
}# end insert_rec

# ohne Leerzeichen:
_ _DATA_ _
verz1/datei4
test
verz1/verz2/verz3/file3
\n\n

<!--EDIT|renee|1111839859-->
OTRS-Erweiterungen (http://feature-addons.de/)
Frankfurt Perlmongers (http://frankfurt.pm/)
--

Unterlagen OTRS-Workshop 2012: http://otrs.perl-services.de/workshop.html
Perl-Entwicklung: http://perl-services.de/
esskar
 2005-03-26 14:24
#52971 #52971
User since
2003-08-04
7321 Artikel
ModeratorIn

user image
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use Data::Dump qw(dump);

my @packeddirs = qw'verz1/datei4 test verz1/verz2/verz3/file3 verz1/file2';
my @unpackeddirs = map {
[split '/', $_]
} @packeddirs;

my @sorteddirs = sort {
my $idx = 0;
my $res = 0;
while(!$res) {
$res = sort_unpacked($idx++, $a, $b)
}
$res;
} @unpackeddirs;

sub sort_unpacked {
my ($idx, $left, $right) = @_;
my $ma = $left->[$idx];
my $mb = $right->[$idx];

defined $ma and defined $mb
? $ma cmp $mb
: defined $ma
? 1
: -1;
}

dump @sorteddirs;


hiermat hast du schonmal die Aufsplittung und sortierung...
das müsste dir eigentlich helfen, oder?
root
 2005-03-28 13:33
#52972 #52972
User since
2003-08-15
120 Artikel
BenutzerIn
[default_avatar]
Vielen Dank!

Ich hab schon viel gegrübelt und ein wenig programmiert. Wenn ich es in meinen Code integrieren, dann kommt eine Hashstruktur heraus. Wenn ich das richtig sehe, dann macht der Data::Dumper aus Listen einen Ausdruck der Liste???

Erstmal vielen Dank. Ich arbeite mich da mal rein. Sieht sehr gut aus!
root
 2005-04-02 22:09
#52973 #52973
User since
2003-08-15
120 Artikel
BenutzerIn
[default_avatar]
Uff! Ich hab jetzt wirklich viel drüber nachgedacht, Zettel voll gemalt und auch ein wenig programmiert. Den Code von Renee habe ich auch fast verstanden!
Mir ist noch nicht so ganz klar, wie Du die mehrdimensionalität hinbekommst.

Was tut diese Zeile genau???
Code: (dl )
    $hashref->{$key} = insert_rec($key,$sub,$hashref->{$key}) if($sub);


Selbst wenn ich es verstehe, hilft es es mir nicht so wirklich weiter, ich brauche hinterher keine mehrdimensionales Hash, sondern ein mehrdimensionaes Array!
Der Umweg über das Hash ist aber gar nicht so schlecht, weil dann keine Doppler passieren können?!

Den Data::Dumper kannte ich noch gar nicht, hab ich mir genauer angesehen, fette Sache!

Ich beschreibe mal nochmal genau mein Problem und zeige dann mal zwei Lösungsansetze die ich hatte:

Ich habe eine Datenbanktabelle für "Dateien". Es sind natürlich nur Datensätze. Ein Datensatz könnte folgende Daten enthalten:
id=1
filename=verz1/verz2/filename
content=bla bla bla
content_type=text

Ich lese die Datensätze so aus, dass ich ein mehrdimensionales Hash erhalte, der Schlüssel ist der filename.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    my $statement="SELECT * FROM orangeFile_files WHERE (vertrag_id=$db_vertrag_id AND status = 'ok') ORDER BY filename";
    my $sth = $dbh->prepare($statement) or die("Kann keine Abfrage ($statement) starten:$DBI::errstr");
    $sth->execute;

    my $row;
    my %flashfile = ();
    my $cnt = 0;

    while ($row = $sth->fetchrow_hashref)
    {
        my %temp=%$row;

        # Nachschauen ob der filename doppelt besteht, dann den aktuellen benutzen
        # da nur gültige aus der DB gelesen werden, können zu alte auch gelesen werden.
        # den älteren also überschrieben:
        if($flashfile{$temp{'filename'}}{'start_time'} > $temp{'start_time'}){
            if($config{'umgebung'} eq "test"){
               
 print "Datei $temp{'filename'} gibt es mit verschiedenen start_time";
            }
            next;
        }else{ # also auch wenn nur ein Datensatz für diesen Flashfile gefunden wird

            # Fügt das gefundende Hash in das HoH ein
            $flashfile{$temp{'filename'}} = { %temp };
            #push @warnungen, "Content of ".$temp{'filename'}." is ".$flashfile{$filename}{'content'};
        }

        $cnt++;
        last if $cnt > 1000000;
    }


Die Keys meines Hashs in der ersten Dimension sind also die aufzubereitenden Daten, dazu muss aber auch noch ein Link kreiert werden, der die id der "Datei" enthält, also irgendwie so aussehen muss:
Code: (dl )
my $link="?name=wert&id=".$flashfile{$filename}{'id'}; # Link um Datei anzuzeigen und zu ändern


Das ist ein Datensatz, alle Datensätze müssen in das beschriebene Array.

Hier meine Verzweifelten Ansätze:
1. quick and dirty - wie es sonst nicht meine Art ist, schäme mich auch ein bisschen:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#! /usr/bin/perl

use strict;
use warnings;
use Data::Dumper;
use CGI::Carp 'fatalsToBrowser';

print "Content-type: text/plain\n\n";

my %hash;
my $zaehler = 0;
while(<DATA>){
    my $path = $_;
    chomp $path;
    next if($path =~ /^\s*?$/);
    $_ =~ s/^\s+//;
    $path =~ s/^\///; #/
    next if(length $path < 2);
    my ($key,$sub) = split(/\//,$path,2);
    print "key: $key | sub: ".$sub."\n";
    unless($sub){
        $hash{$key} = 0;
        next;
    }
    $hash{$key} = insert_rec($key,$sub,$hash{$key});
}


my $outputstruktur = Dumper(\%hash); # Das was für das JS ausgegeben werden soll

# Für das JS die { durch [ ersetzen
$outputstruktur =~ s/}/]/g;
#$outputstruktur =~ s/=>/,/g;
$outputstruktur =~ s/\$VAR1/var TREE_ITEMS/g; # Variablennamen für JS ändern
$outputstruktur =~ s/('\w+') => {/[$1, $zaehler/g; # quick and dirty


# Ausgabe
print $outputstruktur;

# insert_rec builds the hash recursivly.
# Parameters:
#   key of anonymous hash that has to be expanded
#   remaining path
#   reference to hash
sub insert_rec{
    my ($key2,$path,$hashref) = @_;
    my ($key,$sub) = split(/\//,$path,2);
    unless($sub){
        $hashref->{$key} = {};
        return($hashref);
    }
    #print $sub."\n";
    $hashref->{$key} = insert_rec($key,$sub,$hashref->{$key}) if($sub);
    return ($hashref);
}# end insert_rec

# ohne Leerzeichen:

verz1/datei1
verz1/datei2
verz1/datei3
verz1/datei4
file1
verz1/verz2/verz3/file1231
verz1/verz2/verz3/file1232
verz1/verz2/file121
verz1/verz2/file122
verz1/verz2/file123


2. Hab ich versuch die Nummer von dir renee mit nem Array aufzubauen. Ich bekomme aber die Dimensionen, bzw. die Tiefe nicht hin. Ich weiß nicht wie ich $link integrieren soll und hab ein bisschen Probleme, dass die Verzeichnisse in der Unteren Ebene nicht für jeden File aufs neue gepusht werden:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#! /usr/bin/perl

use strict;
use warnings;
use Data::Dumper;
use CGI::Carp 'fatalsToBrowser';

print "Content-type: text/plain\n\n";
my $dimension; # Enthält die aktuelle Dimension des Arrays
my @element_nummer = (); # Enthält die aktuelle Position im Array, bezogen auf die Dimension
my @array_of_arrays = (); # Wird befüllt
my @AoA2 = (); # füll das erste AoA

while(<DATA>){
    $dimension = 0; # reset
    #@element_nummer=(); # reset
    # bisschen was überprüfen und aussortieren
    next if($_ =~ /^\s*?$/);
    $_ =~ s/^\s+//;
    my $path = $_;
    chomp $path;
    $path =~ s/^\///; #/
    next if(length $path < 5);
    # unterstes Verzeichnis nehmen und mit "Rattenschwanz" an Funktion insert_rec übergeben
    my ($key,$sub) = split(/\//,$path,2);
    @AoA2 = (); #reset
    push @array_of_arrays, [insert_rec($key,$sub,"ich bin der Link")];
    $element_nummer[$dimension]++;
}


my $outputstruktur = Dumper(@array_of_arrays); # Das was für das JS ausgegeben werden soll

# Für das JS die { durch { durch [ ersetzen
$outputstruktur =~ s/\$VAR1/var TREE_ITEMS/g; # Variablennamen für JS ändern


# Ausgabe
print $outputstruktur;

sub insert_rec{
    my $key2; # Datei oder Verzeichnisname in der 2. Ebene
    my $path; # Rattenschwanz des Path, überbleibende Verzeichnisse und Dateien
    my $link; # Der Link für das JavaScript
    ($key2,$path,$link) = @_;
    my ($key,$sub) = split(/\//,$path,2); # Wieder aufteilen
    unless($sub){ # Wenn es kein Sub gibt muss es eine Datei sein, also mit Infos zurückgeben
        return($key, $link);
    }
    # Hier komme ich nur hin, wenn $key ein Verzeichnis ist, $sub also existiert, weil ich vorher mit return zurück gesprungen bin.
    # und einmal rekursiv Verzeichnis in Array pushen
    #return (insert_rec($key,$sub,"ich bin ein Verzeichnis"));

    my $arraydimension; # Wie tief soll in das Array gepusht werden?
    $arraydimension = 'push @{ $AoA2';
    # Dimension anhängen
    foreach(@element_nummer){
        $arraydimension .= "[".$_."]";
    }
    $arraydimension .= " }, [ insert_rec('$key','$sub',\"ich bin ein Verzeichnis\") ]";
    print $arraydimension."\n";
    eval($arraydimension);
    # Nächstes Listenelement ist dran
    $element_nummer[$dimension]++;
    $dimension++;
    return(@AoA2);
}# end insert_rec

# Testdaten
# ohne Leerzeichen:

verz1/datei1
verz1/datei2
verz1/datei3
verz1/datei4
file1
verz1/verz2/verz3/file1231
verz1/verz2/verz3/file1232
verz1/verz2/file121
verz1/verz2/file122
verz1/verz2/file123

Ziel ist so ein Print:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var TREE_ITEMS = [
['Home', null,
['test', "Link mit id aus Datenbank"],
['verz1', null,
['file2', "Link mit id aus Datenbank"],
['verz2', null,

['ver3', "Link mit id aus Datenbank",

['file3', "Link mit id aus Datenbank"],

],
],
],
],
];


Alleine packe ich es nicht!!!
Vielen Dank, alleine schon für die Zeit diesen Beitrag in Buchlänge zu lesen :-)

DAAAAAANKE!!!\n\n

<!--EDIT|root|1112465648-->
renee
 2005-04-03 16:47
#52974 #52974
User since
2003-08-04
14371 Artikel
ModeratorIn
[Homepage] [default_avatar]
[quote=root,02.04.2005, 20:09]Mir ist noch nicht so ganz klar, wie Du die mehrdimensionalität hinbekommst.

Was tut diese Zeile genau???
Code: (dl )
    $hashref->{$key} = insert_rec($key,$sub,$hashref->{$key}) if($sub);


[...]

Den Data::Dumper kannte ich noch gar nicht, hab ich mir genauer angesehen, fette Sache!

[...]

Alleine packe ich es nicht!!!
Vielen Dank, alleine schon für die Zeit diesen Beitrag in Buchlänge zu lesen :-)

DAAAAAANKE!!![/quote]
Die Code-Zeile erzeugt genau die Mehrdimensionalität. Das Stichwort hierfür heißt Rekursion.

Ich schau mal, ob ich das ganze auch mit Arrays auf die Schnelle hinbekomme...

CPAN:Data::Dumper ist ein mehr als hilfreiches Modul, wenn man mit komplexen Datenstrukturen zu tun hat! Damit kann man sich die Datenstruktur sehr gut anschauen...
OTRS-Erweiterungen (http://feature-addons.de/)
Frankfurt Perlmongers (http://frankfurt.pm/)
--

Unterlagen OTRS-Workshop 2012: http://otrs.perl-services.de/workshop.html
Perl-Entwicklung: http://perl-services.de/
esskar
 2005-04-03 16:48
#52975 #52975
User since
2003-08-04
7321 Artikel
ModeratorIn

user image
wieso benutzt du nicht einfach meinen ansatz...
dann musst du nur noch @sorteddirs durchlaufen und deine JS füllen!
root
 2005-04-03 21:07
#52976 #52976
User since
2003-08-15
120 Artikel
BenutzerIn
[default_avatar]
@esskar: Ich bin durch deinen Ansatz gar nicht durchgestiegen. Er hat bei mir nur Speicheradressen (oder was diese Zahlenkombinationen seinen sollen) ausgegeben.
Ich verstehe da gar nix!!! Sorry!
Könntest Du es ein bisschen kommentieren oder erklären? Ich würde gerne etwas lernen und nicht nur mit fremden Code ans Ziel kommen! Danke!!!

@renee:
Das es nur rekursiv funktioniert ist mir schon klar, die Funktion muss sich solange selbst aufrufen, bis vom Filenamen nichts mehr über ist. Aber wie genau geht es von Statten?

Du nimmst zuerst einen Path und solange du dich noch in der Verzeichnisangabe des Path befindest rufst du dich selbst auf, übergibst aber an dich nur die nächst höhere Ebene im Path, solange bis zu keinen $sub mehr hast, sprich einen Filenamen hast. Das machst Du mit Hashkeys, also hast Du keine Doppler bei gleichem Mutterverzeichnis des Filename.
Aber wie genau funktioniert das mit der Dimension?
Da bin ich ausgestiegen.
Du hast als key den wert der vorher Sub war. Überschreibst Du somit den Hasheintrag (Key - Wert) und als Wert gibst Du ein Referenz auf sich selbst, also ein Hash. Damit hast Du die mehrdimensionalität???
Diese rekursion hatte ich noch nicht gesehen. Hab nur gesehen, dass sich die Funktion (also sub) wieder selbst aufruft.

Ich müsste also das Array in sich selbst pushen? Dafür müsste ich die Position im Array finden in die ich pushen muss, sonst habe ich Doppler???

Was wäre denn, wenn ich nun einfach das fertige Hash nehme und es jedes Element durchlaufe und entsprechend der Dimensionen in ein Array umwandler?

Vielen Dank euch beiden!!!
coax
 2005-04-03 21:27
#52977 #52977
User since
2003-08-11
457 Artikel
BenutzerIn
[default_avatar]
Vielleicht ist es so etwas verstaendlicher wie das Bsp. mit der Rekursion funktioniert:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/perl

 use strict;
 use warnings;

 my $path = '/dir/dir1.3/dir1.3.1/dir1.3.1.3/file1.3.1.3.1';

 process_path($path);

 sub process_path {
     my $path = shift;                             # Pfad
     my $indent = shift || 0;                      # Einrueckungs-Anzahl

     $path =~ s:^[\/\\]::;                         # entf. fuehrenden (Back-)Slash

     my($super, $sub) = split /[\/\\]/, $path, 2;  # aufteilen in oberen Ordner und Pfadrest

     print '    ' x $indent, "$super\n";

     if($sub) {                                    # wenn es Pfadrest gibt
         process_path($sub, ++$indent);            # ruf mich erneut mit Pfadrest auf
         return;
         print '    ' x $indent, "$sub\n";
     }
 }
,,Das perlt aber heute wieder...'' -- Dittsche
coax
 2005-04-03 23:30
#52978 #52978
User since
2003-08-11
457 Artikel
BenutzerIn
[default_avatar]
Eine vollfunktionierende aber ineffiziente Version:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!/usr/bin/perl

 use strict;
 use warnings;

 use Data::Dumper;

 our $DEBUG = 0;       # Debugging: Aus = 0; An = 1 oder 2
 our $SHIFTWIDTH = 4;

 # Simuliert Datenbankabfrage
 my(@rows) =  map { my %h; @h{ qw(id filename content content_type) } = @$_; \%h }
         [1, '/dir1/dir1.1/dir1.1.1/file1.1.1.1', 'bla bla bla',   'text'],
         [2, '/dir1/dir1.2/dir1.2.1/file1.2.1.1', 'blu bla blubb', 'text'],
         [4, '/dir1/file1',                       'blu bla bla',   'text'],
         [5, '/dir1/file2',                       'blu bla ble',   'text'],
         [3, '/dir2/dir2.1/dir2.1.1/file2.1.1.3', 'bli blu bla',   'text'],
         [6, 'file1',                             'bar bla blo',   'text'],
         [7, 'afile2',                            'another file',  'text'],
         [8, 'bfile2',                            'bnother file',  'text'];

 my $tree;
 while(my $row = shift @rows) {                             # jede Datenzeile
     $tree = process_data($row->{'filename'}, $row, $tree); # verarbeiten und Baum hinzufuegen
 }

 print Dumper($tree), '-'x25, "\n" if $DEBUG;

 print <<EOT1;
var TREE_ITEMS = [
EOT1

 # erzeuge JS-Code
 build_js_code($tree);

 print <<EOT2;
];
EOT2
 
 sub process_data {
     my($path, $row_data, $tree) = @_;

     $tree = {'type' => 'dir'} unless $tree;

     $path =~ s:^[\\/]::;

     my($super, $sub) = split /[\/\\]/, $path, 2;

     if($sub && $sub ne '') {
         $tree->{'items'}->{$super} = process_data($sub, $row_data, {'dir' => 1});
     } else {
         $tree->{'items'}->{$super} = {'file' => 1, 'data' => $row_data };
     }

     return $tree;
 }

 sub build_js_code {
     my $tree = shift;
     my $indent = shift || 1;
         foreach my $key (sort {return $a cmp $b if $tree->{'items'}->{$a}->{'dir'}
                                                 && $tree->{'items'}->{$b}->{'dir'};
                                return $a cmp $b if $tree->{'items'}->{$a}->{'file'}
                                                 && $tree->{'items'}->{$b}->{'file'};
                                return $tree->{'items'}->{$a}->{'dir'} ? -1 : 1;}
                          keys %{ $tree->{'items'} } ) {

         if( $tree->{'items'}->{$key}->{'dir'} ) {
             print ' ' x ($SHIFTWIDTH * $indent),
                   "['", $key, ", null,\n";
             build_js_code( $tree->{'items'}->{$key}, $indent + 1);
         } else {
             print ' ' x  ($SHIFTWIDTH * $indent),
                   "['", $key, "', '", $tree->{'items'}->{$key}->{'data'}->{'content'}, "']\n";
         }
     }
     
 }

Die erste rekursive Funktion bildet den Verzeichnisbaum in Perlstruktur die zweite sortiert den Baum und gibt den JS-Code aus (laesst sich mit Sicherheit auch kuerzer schreiben).
Durch die vielen Funktionsaufrufe ist der Code aber seeehhr langsam - Esskar's Variante duerfte um einiges schneller sein.

edit: perl-Tag hat Code manipuliert\n\n

<!--EDIT|coax|1112556828-->
,,Das perlt aber heute wieder...'' -- Dittsche
<< |< 1 2 >| >> 14 Einträge, 2 Seiten



View all threads created 2005-03-26 12:20.