Multi-level list generation

S

Steve

I have a question about how to generate a multi-level (nested) list
structure by perl. I currently have a 2-level
<ul><li><ul><li></li></ul></li></ul> structure produced via a perl script,
which works fine for its purpose. An example HTML output by the existing
script is:

<ul>
<li><a href=subject_1.0.html>Subject 1.0</a>
<ul>
<li><a href=page_1.1.html>Page 1.1</a></li>
<li><a href=page_1.2.html>Page 1.2</a></li>
<li><a href=page_1.3.html>Page 1.3</a></li>
</ul>
</li>
</ul>

There's more non-relevant code that I've stripped for a bit of clarity,
such as CSS etc. In fact, the actual HTML code is largely irrelavant.
Anyway, a barebone version of the perl procedure generating the above is:

#!/usr/bin/perl -w

use Tie::IxHash;
use strict;
use warnings;

my $object1 = tie my %listoflinks, "Tie::IxHash";

%listoflinks = ('subject_1.0.html', => 'Subject 1.0',
'page1.1.html', => 'Page 1.1',
'page1.2.html', => 'Page 1.2',
'page1.3.html', => 'Page 1.3');

for (\%listoflinks) {
my $firstkey = each %$_;

print "<ul>\n"; # open 1st UL

print "<li><a href=$firstkey>$listoflinks{$firstkey}</a>\n"; # open 1st LI

print "<ul>\n"; # open nested UL

while ( local $_ = each %$_ ) {
{ print "<li><a href=$_>$listoflinks{$_}</a></li> \n" } # print some LI's
}

print "</ul>\n"; # close nested UL
print "</li>\n"; # close first LI
print "</ul>\n"; # close first UL

}

The above procedure was put together with good help from this group ages
ago. As mentioned, the code takes care of the 2-level list structure and
does so by fetching the $firstkey from the array entries or LoH and
inserting the needed opening and closing UL's and LI's in the right places.

However, I'm not quite sure how to change the script to generate a third
level, such as:

<ul>
<li><a href=subject_1.0.html>Subject 1.0</a>
<ul>
<li><a href=page_1.1.html>Page 1.1</a></li>
<li><a href=page_1.2.html>Page 1.2</a></li>
<li><a href=page_1.3.html>Page 1.3</a></li>
<li><a href=subject_2.0.html>Subject 2.0</a>
<ul>
<li><a href=page_2.1.html>Page 2.1</a></li>
<li><a href=page_2.1.html>Page 2.2</a></li>
</ul>
</li>
</ul>
</li>
</ul>

Or for example, the same structure, with another two second-level list
items at the end:

<ul>
<li><a href=subject_1.0.html>Subject 1.0</a>
<ul>
<li><a href=page_1.1.html>Page 1.1</a></li>
<li><a href=page_1.2.html>Page 1.2</a></li>
<li><a href=page_1.3.html>Page 1.3</a></li>
<li><a href=subject_2.0.html>Subject 2.0</a>
<ul>
<li><a href=page_2.1.html>Page 2.1</a></li>
<li><a href=page_2.1.html>Page 2.2</a></li>
</ul>
</li>
<li><a href=page_1.4.html>Page 1.4</a></li>
<li><a href=page_1.5.html>Page 1.5</a></li>
</ul>
</li>
</ul>

Naturally a different array structure would be required in my %listoflinks
to output the above. Any advise or examples how this may be pieced together
would be most helpful.

Perhaps someone has a procedure in use that does something similar already?

Many thanks,
Tuxedo

NB: System load efficiency is not an issue, as the procedure will run only
occasionally on a local machine to generate HTML sent onto a web server in
static format. In other words, the script will not run against any real web
page requests. The procedure is simply meant to be an easy maintenance tool.

I use recursive routines a *lot* in printing out nested data structures,
and they are your friend in cases like this...

The below is:
1) not tested in any way,
2) may not even compile,
3) and is just a concept.

%hash = ( whatever, too lazy to make one );

recurse_hash( \%hash );

sub recurse_hash {
my $refhash = shift;
$refhash or return '';

print "<ul>\n";

while( keys %{$refhash} ){
if( ref $refhash->{$_} eq 'HASH' ){
recurse_hash( $refhash->{$_} );
}
else{
print "<li>$refhash->{$_}</li>\n";
}
}

print "</ul>\n";
}

The beauty of a recursive is it flat doesn't matter how many levels deep
the data structure is.

The downside is it flat doesn't matter how many levels deep the
recursive 'thinks' the data structure is and sloppy programming can
bite you big time..... infinite recursion anyone?

hth,

\s
 
T

Tuxedo

I have a question about how to generate a multi-level (nested) list
structure by perl. I currently have a 2-level
<ul><li><ul><li></li></ul></li></ul> structure produced via a perl script,
which works fine for its purpose. An example HTML output by the existing
script is:

<ul>
<li><a href=subject_1.0.html>Subject 1.0</a>
<ul>
<li><a href=page_1.1.html>Page 1.1</a></li>
<li><a href=page_1.2.html>Page 1.2</a></li>
<li><a href=page_1.3.html>Page 1.3</a></li>
</ul>
</li>
</ul>

There's more non-relevant code that I've stripped for a bit of clarity,
such as CSS etc. In fact, the actual HTML code is largely irrelavant.
Anyway, a barebone version of the perl procedure generating the above is:

#!/usr/bin/perl -w

use Tie::IxHash;
use strict;
use warnings;

my $object1 = tie my %listoflinks, "Tie::IxHash";

%listoflinks = ('subject_1.0.html', => 'Subject 1.0',
'page1.1.html', => 'Page 1.1',
'page1.2.html', => 'Page 1.2',
'page1.3.html', => 'Page 1.3');

for (\%listoflinks) {
my $firstkey = each %$_;

print "<ul>\n"; # open 1st UL

print "<li><a href=$firstkey>$listoflinks{$firstkey}</a>\n"; # open 1st LI

print "<ul>\n"; # open nested UL

while ( local $_ = each %$_ ) {
{ print "<li><a href=$_>$listoflinks{$_}</a></li> \n" } # print some LI's
}

print "</ul>\n"; # close nested UL
print "</li>\n"; # close first LI
print "</ul>\n"; # close first UL

}

The above procedure was put together with good help from this group ages
ago. As mentioned, the code takes care of the 2-level list structure and
does so by fetching the $firstkey from the array entries or LoH and
inserting the needed opening and closing UL's and LI's in the right places.

However, I'm not quite sure how to change the script to generate a third
level, such as:

<ul>
<li><a href=subject_1.0.html>Subject 1.0</a>
<ul>
<li><a href=page_1.1.html>Page 1.1</a></li>
<li><a href=page_1.2.html>Page 1.2</a></li>
<li><a href=page_1.3.html>Page 1.3</a></li>
<li><a href=subject_2.0.html>Subject 2.0</a>
<ul>
<li><a href=page_2.1.html>Page 2.1</a></li>
<li><a href=page_2.1.html>Page 2.2</a></li>
</ul>
</li>
</ul>
</li>
</ul>

Or for example, the same structure, with another two second-level list
items at the end:

<ul>
<li><a href=subject_1.0.html>Subject 1.0</a>
<ul>
<li><a href=page_1.1.html>Page 1.1</a></li>
<li><a href=page_1.2.html>Page 1.2</a></li>
<li><a href=page_1.3.html>Page 1.3</a></li>
<li><a href=subject_2.0.html>Subject 2.0</a>
<ul>
<li><a href=page_2.1.html>Page 2.1</a></li>
<li><a href=page_2.1.html>Page 2.2</a></li>
</ul>
</li>
<li><a href=page_1.4.html>Page 1.4</a></li>
<li><a href=page_1.5.html>Page 1.5</a></li>
</ul>
</li>
</ul>

Naturally a different array structure would be required in my %listoflinks
to output the above. Any advise or examples how this may be pieced together
would be most helpful.

Perhaps someone has a procedure in use that does something similar already?

Many thanks,
Tuxedo

NB: System load efficiency is not an issue, as the procedure will run only
occasionally on a local machine to generate HTML sent onto a web server in
static format. In other words, the script will not run against any real web
page requests. The procedure is simply meant to be an easy maintenance tool.
 
S

Steve

Thanks for the above solution! However, it is a bit difficult for me to
figure exactly how the %hash = (part may be composed) to generate a
multi-level list structure. Any additional pointers anyone?

Tuxedo

Well.....

my %hash = (
key1 => { subkey => 'value',
subhash => {
subsubkey1 => 'value',
subsubkey2 => 'another value',
},
},
key2 => 'value',
etc => {
},

);

But, 'more better' would be to go a reliable (and vetted) source like:

http://perldoc.perl.org/perldsc.html

hth,

\s
 
J

Jürgen Exner

Tuxedo said:
I have a question about how to generate a multi-level (nested) list
structure by perl. I currently have a 2-level
<ul><li><ul><li></li></ul></li></ul> structure produced via a perl script,
which works fine for its purpose. An example HTML output by the existing
script is: [...]

The above procedure was put together with good help from this group ages
ago. As mentioned, the code takes care of the 2-level list structure and
does so by fetching the $firstkey from the array entries or LoH and
inserting the needed opening and closing UL's and LI's in the right places.

However, I'm not quite sure how to change the script to generate a third
level, such as:

If you keep adding additional levels then recursion is your friend:
(sketch in pseudo-code, transfer into actual Perl is left as an
excercise):

sub print_elem {
#arguments: single argument, either reference to list or single item
if (ref $_[0]) { # this element is a reference to a nested list
print_header(); #print header for the sub-list
for (@_) {
print_elem(@{$_}); #print each element of the sub-list
}
print_footer(); #print footer for the sub-list

} else { #this element is a single item
print_item($_[0]; #just print it
}
)


jue
 
T

Tuxedo

Steve said:
I use recursive routines a *lot* in printing out nested data structures,
and they are your friend in cases like this...

The below is:
1) not tested in any way,
2) may not even compile,
3) and is just a concept.

%hash = ( whatever, too lazy to make one );

recurse_hash( \%hash );

sub recurse_hash {
my $refhash = shift;
$refhash or return '';

print "<ul>\n";

while( keys %{$refhash} ){
if( ref $refhash->{$_} eq 'HASH' ){
recurse_hash( $refhash->{$_} );
}
else{
print "<li>$refhash->{$_}</li>\n";
}
}

print "</ul>\n";
}

The beauty of a recursive is it flat doesn't matter how many levels deep
the data structure is.

The downside is it flat doesn't matter how many levels deep the
recursive 'thinks' the data structure is and sloppy programming can
bite you big time..... infinite recursion anyone?

hth,

\s

Thanks for the above solution! However, it is a bit difficult for me to
figure exactly how the %hash = (part may be composed) to generate a
multi-level list structure. Any additional pointers anyone?

Tuxedo
 
T

Tuxedo

Jürgen Exner said:
Tuxedo said:
I have a question about how to generate a multi-level (nested) list
structure by perl. I currently have a 2-level
<ul><li><ul><li></li></ul></li></ul> structure produced via a perl
script, which works fine for its purpose. An example HTML output by the
existing script is: [...]

The above procedure was put together with good help from this group ages
ago. As mentioned, the code takes care of the 2-level list structure and
does so by fetching the $firstkey from the array entries or LoH and
inserting the needed opening and closing UL's and LI's in the right
places.

However, I'm not quite sure how to change the script to generate a third
level, such as:

If you keep adding additional levels then recursion is your friend:
(sketch in pseudo-code, transfer into actual Perl is left as an
excercise):

sub print_elem {
#arguments: single argument, either reference to list or single item
if (ref $_[0]) { # this element is a reference to a nested list
print_header(); #print header for the sub-list
for (@_) {
print_elem(@{$_}); #print each element of the sub-list
}
print_footer(); #print footer for the sub-list

} else { #this element is a single item
print_item($_[0]; #just print it
}
)


jue

This looks like a good solution. Yet difficult for me to translate into
functioning code against an array of hash values or items.

Any bit of clearer description would be greatly appreciated.

Thanks,
Tuxedo
 
C

ccc31807

I have a question about how to generate a multi-level (nested) list
structure by perl. I currently have a 2-level
<ul><li><ul><li></li></ul></li></ul> structure produced via a perl script,
which works fine for its purpose. An example HTML output by the existing
script is:

Mark Jason Dominus, in 'Higher Order Perl' has a section on parsing
HTML in chapter 1. You can download the book over the internet, but
it's well worth buying, and I would encourage you to do so.

CC
 
U

Uri Guttman

c> Mark Jason Dominus, in 'Higher Order Perl' has a section on parsing
c> HTML in chapter 1. You can download the book over the internet, but
c> it's well worth buying, and I would encourage you to do so.

the OP isn't parsing but generating html. parsing it should be done with
a module. generating it is done well with templates but he still needs
to learn data structures to work with them. regardless of the
technology, nested html needs nested data which means perl data
structures. they are used in some many perl programs that they are
critical to learn early one. perlreftut, perldsc and perllol are
required reading from the perl docs.

uri
 
T

Tuxedo

Steve said:
Well.....

my %hash = (
key1 => { subkey => 'value',
subhash => {
subsubkey1 => 'value',
subsubkey2 => 'another value',
},
},
key2 => 'value',
etc => {
},

);

But, 'more better' would be to go a reliable (and vetted) source like:

http://perldoc.perl.org/perldsc.html

hth,

\s


Thanks for the hash example and perldoc resource. However, it's still a bit
confusing. To gain a bit further understanding I tried to run your script
'as is' but it failed to compile, as of course you warned me it may do. In
saving your script as a file named testrun.pl and running it, the following
errors are repeatedly returned to the shell until I hit Ctrl+C:

<li></li>
Use of uninitialized value in hash element at ./testrun.pl line 27.
Use of uninitialized value in hash element at ./testrun.pl line 31.
Use of uninitialized value in concatenation (.) or string at ./testrun.pl
line 31.
<li></li>
Use of uninitialized value in hash element at ./testrun.pl line 27.
Use of uninitialized value in hash element at ./testrun.pl line 31.
Use of uninitialized value in concatenation (.) or string at ./testrun.pl
line 31.
<li></li>

etc.

Unfortunately I do not understand the meaning of the above errors and if I
redirect the output to a file, like in ./testrun.pl > file.txt, the file
contains a long list of empty <li></li> containers after an opening <ul>:

<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>

etc...

Below is the exact copy of what I tried to run:

#!/usr/bin/perl -w

use strict;
use warnings;

my %hash = (
key1 => { subkey => 'value',
subhash => {
subsubkey1 => 'value',
subsubkey2 => 'another value',
},
},
key2 => 'value',

);


recurse_hash( \%hash );

sub recurse_hash {
my $refhash = shift;
$refhash or return '';

print "<ul>\n";

while( keys %{$refhash} ){
if( ref $refhash->{$_} eq 'HASH' ){
recurse_hash( $refhash->{$_} );
}
else{
print "<li>$refhash->{$_}</li>\n";
}
}

print "</ul>\n";
}

Anyone knows where the error(s) in the above procedure are?

Thanks again,
Tuxedo
 
T

Tuxedo

ccc31807 said:
Mark Jason Dominus, in 'Higher Order Perl' has a section on parsing
HTML in chapter 1. You can download the book over the internet, but
it's well worth buying, and I would encourage you to do so.

CC

Thanks for the tip. Seems to be a good book!

Tuxedo
 
T

Tuxedo

Uri said:
c> Mark Jason Dominus, in 'Higher Order Perl' has a section on parsing
c> HTML in chapter 1. You can download the book over the internet, but
c> it's well worth buying, and I would encourage you to do so.

the OP isn't parsing but generating html. parsing it should be done with
a module. generating it is done well with templates but he still needs
to learn data structures to work with them. regardless of the
technology, nested html needs nested data which means perl data
structures. they are used in some many perl programs that they are
critical to learn early one. perlreftut, perldsc and perllol are
required reading from the perl docs.

uri

Will look into perlreftut, perldsc and perllol. My task may seem trivial,
but as you say, the relevant data structures are needed, which makes this
one a hard nut to crack without a fairly deep level of perl knowledge.

Tuxedo
 
S

Steve

Thanks for the hash example and perldoc resource. However, it's still a bit
confusing. To gain a bit further understanding I tried to run your script
'as is' but it failed to compile, as of course you warned me it may do. In
saving your script as a file named testrun.pl and running it, the following
errors are repeatedly returned to the shell until I hit Ctrl+C:

<li></li>
Use of uninitialized value in hash element at ./testrun.pl line 27.
Use of uninitialized value in hash element at ./testrun.pl line 31.
Use of uninitialized value in concatenation (.) or string at ./testrun.pl
line 31.
<li></li>
Use of uninitialized value in hash element at ./testrun.pl line 27.
Use of uninitialized value in hash element at ./testrun.pl line 31.
Use of uninitialized value in concatenation (.) or string at ./testrun.pl
line 31.
<li></li>

etc.

Unfortunately I do not understand the meaning of the above errors and if I
redirect the output to a file, like in ./testrun.pl> file.txt, the file
contains a long list of empty<li></li> containers after an opening<ul>:

<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>

etc...

Below is the exact copy of what I tried to run:

#!/usr/bin/perl -w

use strict;
use warnings;

my %hash = (
key1 => { subkey => 'value',
subhash => {
subsubkey1 => 'value',
subsubkey2 => 'another value',
},
},
key2 => 'value',

);


recurse_hash( \%hash );

sub recurse_hash {
my $refhash = shift;
$refhash or return '';

print "<ul>\n";

while( keys %{$refhash} ){
if( ref $refhash->{$_} eq 'HASH' ){
recurse_hash( $refhash->{$_} );
}
else{
print "<li>$refhash->{$_}</li>\n";
}
}

print "</ul>\n";
}

Anyone knows where the error(s) in the above procedure are?

Thanks again,
Tuxedo

OK, here:
1) tested
2) compiles
3) works


#! /usr/bin/perl

use warnings;
use strict;

my %one = (
onek1v1 => 'hash one value 1',
onek2v1 => 'hash one value 2',
);

my %oneref = (
onerefk1v1 => { anotherlevel => 'hash one down two' },
onerefval => 'hash one value down one',
);

$one{'downone'} = \%oneref;

my %two = (
twok1v1 => 'hash two value 1',
twok2v1 => 'hash two value 2',
);

my %three = (
threek1v1 => 'hash three value 1',
threek2v1 => 'hash three value 2',
);

my %main_hash = (
one => \%one,
two => \%two,
three => \%three,
);

recurse_hash( \%main_hash );

our $spct = 0; ## track leading spaces count

sub recurse_hash {

my $refhash = shift;
$refhash or return '';

$spct and print ' ' x $spct; # spacecount x spacespace

print "<ul>\n";

$spct += 1;

for( keys %{$refhash} ){

if( ref $refhash->{$_} eq 'HASH' ){

$spct += 1;

recurse_hash( $refhash->{$_} );

$spct -= 1;
}
else{

$spct+= 1;
print ' ' x $spct;

print "<li>$refhash->{$_}</li>\n";

$spct -= 1;
}
}

$spct -= 1;

print ' ' x $spct;

print "</ul>\n";

}

exit;


Command line Output:

<ul>
<ul>
<li>hash three value 1</li>
<li>hash three value 2</li>
</ul>
<ul>
<li>hash one value 2</li>
<ul>
<li>hash one value down one</li>
<ul>
<li>hash one down two</li>
</ul>
</ul>
<li>hash one value 1</li>
</ul>
<ul>
<li>hash two value 2</li>
<li>hash two value 1</li>
</ul>
</ul>



You'll note the order is NOT preserved as I'm not using Tie::IxHash.

Anyway, play with the script portion to get a feel for what is going on.
Add/Remove hash refs, whatever.

Once the concept starts sinking in you will have taken a major step in
understanding/using Perl.

I read somewhere once that if you are not using hashes you are not doing
Perl.

Spot on.

\s
 
S

Steve

Thanks for the hash example and perldoc resource. However, it's still a bit
confusing. To gain a bit further understanding I tried to run your script
'as is' but it failed to compile, as of course you warned me it may do. In
saving your script as a file named testrun.pl and running it, the following
errors are repeatedly returned to the shell until I hit Ctrl+C:

<li></li>
Use of uninitialized value in hash element at ./testrun.pl line 27.
Use of uninitialized value in hash element at ./testrun.pl line 31.
Use of uninitialized value in concatenation (.) or string at ./testrun.pl
line 31.
<li></li>
Use of uninitialized value in hash element at ./testrun.pl line 27.
Use of uninitialized value in hash element at ./testrun.pl line 31.
Use of uninitialized value in concatenation (.) or string at ./testrun.pl
line 31.
<li></li>

etc.

Unfortunately I do not understand the meaning of the above errors and if I
redirect the output to a file, like in ./testrun.pl> file.txt, the file
contains a long list of empty<li></li> containers after an opening<ul>:

<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>

etc...

Below is the exact copy of what I tried to run:

#!/usr/bin/perl -w

use strict;
use warnings;

my %hash = (
key1 => { subkey => 'value',
subhash => {
subsubkey1 => 'value',
subsubkey2 => 'another value',
},
},
key2 => 'value',

);


recurse_hash( \%hash );

sub recurse_hash {
my $refhash = shift;
$refhash or return '';

print "<ul>\n";

while( keys %{$refhash} ){
if( ref $refhash->{$_} eq 'HASH' ){
recurse_hash( $refhash->{$_} );
}
else{
print "<li>$refhash->{$_}</li>\n";
}
}

print "</ul>\n";
}

Anyone knows where the error(s) in the above procedure are?

Thanks again,
Tuxedo

OK, here:
1) tested
2) compiles
3) works


#! /usr/bin/perl

use warnings;
use strict;

my %one = (
onek1v1 => 'hash one value 1',
onek2v1 => 'hash one value 2',
);

my %oneref = (
onerefk1v1 => { anotherlevel => 'hash one down two' },
onerefval => 'hash one value down one',
);

$one{'downone'} = \%oneref;

my %two = (
twok1v1 => 'hash two value 1',
twok2v1 => 'hash two value 2',
);

my %three = (
threek1v1 => 'hash three value 1',
threek2v1 => 'hash three value 2',
);

my %main_hash = (
one => \%one,
two => \%two,
three => \%three,
);

recurse_hash( \%main_hash );

our $spct = 0; ## track leading spaces count

sub recurse_hash {

my $refhash = shift;
$refhash or return '';

$spct and print ' ' x $spct; # spacecount x spacespace

print "<ul>\n";

$spct += 1;

for( keys %{$refhash} ){

if( ref $refhash->{$_} eq 'HASH' ){

$spct += 1;

recurse_hash( $refhash->{$_} );

$spct -= 1;
}
else{

$spct+= 1;
print ' ' x $spct;

print "<li>$refhash->{$_}</li>\n";

$spct -= 1;
}
}

$spct -= 1;

print ' ' x $spct;

print "</ul>\n";

}

exit;


Command line Output:

<ul>
<ul>
<li>hash three value 1</li>
<li>hash three value 2</li>
</ul>
<ul>
<li>hash one value 2</li>
<ul>
<li>hash one value down one</li>
<ul>
<li>hash one down two</li>
</ul>
</ul>
<li>hash one value 1</li>
</ul>
<ul>
<li>hash two value 2</li>
<li>hash two value 1</li>
</ul>
</ul>



You'll note the order is NOT preserved as I'm not using Tie::IxHash.

Anyway, play with the script portion to get a feel for what is going on.
Add/Remove hash refs, whatever.

Once the concept starts sinking in you will have taken a major step in
understanding/using Perl.

I read somewhere once that if you are not using hashes you are not doing
Perl.

Spot on.

\s
 
S

Steve

Will look into perlreftut, perldsc and perllol. My task may seem trivial,
but as you say, the relevant data structures are needed, which makes this
one a hard nut to crack without a fairly deep level of perl knowledge.

Tuxedo


Uri is right in his response that you *must* learn to deal with complex
data structures.

I've been using Perl to generate web pages since the mid '90's and I can
promise you that if you take the time to learn how to deal with nested
data, how to access it, and how to create it, you will be amazed at how
much simpler your job becomes.

So, suck it up and go for it. It will only hurt for a little while. :)

\s
 
U

Uri Guttman

T> Will look into perlreftut, perldsc and perllol. My task may seem trivial,
T> but as you say, the relevant data structures are needed, which makes this
T> one a hard nut to crack without a fairly deep level of perl knowledge.

i consider perl refs and data structure mid-level perl and not deep
knowledge. they aren't that hard to learn and they are used all the time
which make them important to learn.

as for templating html, there are many choices. i, of course, recommend
Template::Simple which you can learn quickly and will help in this
task. regardless of the method you need to learn perl data structures.

uri
 
S

sln

I have a question about how to generate a multi-level (nested) list
structure by perl. I currently have a 2-level
<ul><li><ul><li></li></ul></li></ul> structure produced via a perl script,
which works fine for its purpose. An example HTML output by the existing
script is:

<ul>
<li><a href=subject_1.0.html>Subject 1.0</a>
<ul>
<li><a href=page_1.1.html>Page 1.1</a></li>
<li><a href=page_1.2.html>Page 1.2</a></li>
<li><a href=page_1.3.html>Page 1.3</a></li>
</ul>
</li>
</ul>

I don't have Tie::IxHash and it is not part of the core.
After commenting it out and running the code, it prints:

<ul>
<li><a href=page1.3.html>Page 1.3</a>
<ul>
<li><a href=subject_1.0.html>Subject 1.0</a></li>
<li><a href=page1.2.html>Page 1.2</a></li>
<li><a href=page1.1.html>Page 1.1</a></li>
</ul>
</li>
</ul>

This doesen't look like your output to me.
There's more non-relevant code that I've stripped for a bit of clarity,
such as CSS etc. In fact, the actual HTML code is largely irrelavant.
Anyway, a barebone version of the perl procedure generating the above is:

#!/usr/bin/perl -w

use Tie::IxHash;
use strict;
use warnings;

my $object1 = tie my %listoflinks, "Tie::IxHash";

%listoflinks = ('subject_1.0.html', => 'Subject 1.0',
'page1.1.html', => 'Page 1.1',
'page1.2.html', => 'Page 1.2',
'page1.3.html', => 'Page 1.3');

for (\%listoflinks) {
my $firstkey = each %$_;

print "<ul>\n"; # open 1st UL

print "<li><a href=$firstkey>$listoflinks{$firstkey}</a>\n"; # open 1st LI

print "<ul>\n"; # open nested UL

while ( local $_ = each %$_ ) {
{ print "<li><a href=$_>$listoflinks{$_}</a></li> \n" } # print some LI's
}

print "</ul>\n"; # close nested UL
print "</li>\n"; # close first LI
print "</ul>\n"; # close first UL

}

The above procedure was put together with good help from this group ages
ago.

This looks to be fairly junky code. There is nothing but a linear
generation of html. It might as well have been put together with a
here doc statement.
As mentioned, the code takes care of the 2-level list structure and
does so by fetching the $firstkey from the array entries or LoH and
inserting the needed opening and closing UL's and LI's in the right places.

However, I'm not quite sure how to change the script to generate a third
level, such as:

You havent even been to the second generation, so far its
really been 1 dimensional.
<ul>
<li><a href=subject_1.0.html>Subject 1.0</a>
<ul>
<li><a href=page_1.1.html>Page 1.1</a></li>
<li><a href=page_1.2.html>Page 1.2</a></li>
<li><a href=page_1.3.html>Page 1.3</a></li>
<li><a href=subject_2.0.html>Subject 2.0</a>
<ul>
<li><a href=page_2.1.html>Page 2.1</a></li>
<li><a href=page_2.1.html>Page 2.2</a></li>
</ul>
</li>
</ul>
</li>
</ul>

Or for example, the same structure, with another two second-level list
items at the end:

<ul>
<li><a href=subject_1.0.html>Subject 1.0</a>
<ul>
<li><a href=page_1.1.html>Page 1.1</a></li>
<li><a href=page_1.2.html>Page 1.2</a></li>
<li><a href=page_1.3.html>Page 1.3</a></li>
<li><a href=subject_2.0.html>Subject 2.0</a>
<ul>
<li><a href=page_2.1.html>Page 2.1</a></li>
<li><a href=page_2.1.html>Page 2.2</a></li>
</ul>
</li>
<li><a href=page_1.4.html>Page 1.4</a></li>
<li><a href=page_1.5.html>Page 1.5</a></li>
</ul>
</li>
</ul>

Naturally a different array structure would be required in my %listoflinks
to output the above. Any advise or examples how this may be pieced together
would be most helpful.

Perhaps someone has a procedure in use that does something similar already?

If you want to put together a dynamic html generator, you
will need to write a mini engine to do that.

-sln
 
T

Tuxedo

I don't have Tie::IxHash and it is not part of the core.
After commenting it out and running the code, it prints:

<ul>
<li><a href=page1.3.html>Page 1.3</a>
<ul>
<li><a href=subject_1.0.html>Subject 1.0</a></li>
<li><a href=page1.2.html>Page 1.2</a></li>
<li><a href=page1.1.html>Page 1.1</a></li>
</ul>
</li>
</ul>

This doesen't look like your output to me.

Yes, because Tie::IxHash puts things in order. Without, %listoflinks will
appear in a seemingly random order.
This looks to be fairly junky code. There is nothing but a linear
generation of html. It might as well have been put together with a
here doc statement.

It fulfilled the purpose it was meant to serve, call it junk or not.
You havent even been to the second generation, so far its
really been 1 dimensional.

Counting the ground floor so to speak, the structure produced by the
existing script was the following:

<ul>
<li>first
<ul>
<li>second</li>
</ul>
</li>
</ul>

Instead what I need is:

<ul>
<li>first
<ul>
<li>second
<ul>
<li>third</li>
</ul>
</li>
</ul>
</li>
</ul>

The current script is not capable of two floors, partly because of the data
structure and the fact that it simply fetches the firstkey to insert the
relevant HTML code in the right places.
If you want to put together a dynamic html generator, you
will need to write a mini engine to do that.

Yes, it sounds like a need a nested list engine of the best make :)

Tuxedo
 
T

Tuxedo

Steve said:
OK, here:
1) tested
2) compiles
3) works


#! /usr/bin/perl

use warnings;
use strict;

my %one = (
onek1v1 => 'hash one value 1',
onek2v1 => 'hash one value 2',
);

my %oneref = (
onerefk1v1 => { anotherlevel => 'hash one down two' },
onerefval => 'hash one value down one',
);

$one{'downone'} = \%oneref;

my %two = (
twok1v1 => 'hash two value 1',
twok2v1 => 'hash two value 2',
);

my %three = (
threek1v1 => 'hash three value 1',
threek2v1 => 'hash three value 2',
);

my %main_hash = (
one => \%one,
two => \%two,
three => \%three,
);

recurse_hash( \%main_hash );

our $spct = 0; ## track leading spaces count

sub recurse_hash {

my $refhash = shift;
$refhash or return '';

$spct and print ' ' x $spct; # spacecount x spacespace

print "<ul>\n";

$spct += 1;

for( keys %{$refhash} ){

if( ref $refhash->{$_} eq 'HASH' ){

$spct += 1;

recurse_hash( $refhash->{$_} );

$spct -= 1;
}
else{

$spct+= 1;
print ' ' x $spct;

print "<li>$refhash->{$_}</li>\n";

$spct -= 1;
}
}

$spct -= 1;

print ' ' x $spct;

print "</ul>\n";

}

exit;


Command line Output:

<ul>
<ul>
<li>hash three value 1</li>
<li>hash three value 2</li>
</ul>
<ul>
<li>hash one value 2</li>
<ul>
<li>hash one value down one</li>
<ul>
<li>hash one down two</li>
</ul>
</ul>
<li>hash one value 1</li>
</ul>
<ul>
<li>hash two value 2</li>
<li>hash two value 1</li>
</ul>
</ul>



You'll note the order is NOT preserved as I'm not using Tie::IxHash.

Anyway, play with the script portion to get a feel for what is going on.
Add/Remove hash refs, whatever.

Once the concept starts sinking in you will have taken a major step in
understanding/using Perl.

I read somewhere once that if you are not using hashes you are not doing
Perl.

Spot on.

\s


Thanks for this excellent example!

The only odd thing I see at first sight is that the first <ul> is not
followed directly by an <li> at any level, which is the intended structure
of the particular HTML list. Instead, there is a <ul><ul> structure. I
think I need to apply Tie::IxHash to see through this a bit better. I will
tinker with it and will surely find it a useful learning experience :)

Thanks again,
Tuxedo
 
T

Tuxedo

Tad said:
None of the messages you've shown indicate a failure to compile.

What makes you think compilation failed?




None of those are error messages.

They are warning messages.




Even after looking up the messages in Perl's standard docs?

perldoc perldiag

Yes, warnings. You're right. Will take a note of 'perldoc perldiag'.
If the program ran, then it must have compiled successfully...

It sure ran, in fact it didn't stop running without Ctrl+C. As with errors
vs. warnings, just wrong use of terminology on my part.

Thanks for putting the records straight!

Tuxedo
 
T

Tuxedo

Uri said:
T> Will look into perlreftut, perldsc and perllol. My task may seem
trivial, T> but as you say, the relevant data structures are needed,
which makes this T> one a hard nut to crack without a fairly deep level
of perl knowledge.

i consider perl refs and data structure mid-level perl and not deep
knowledge. they aren't that hard to learn and they are used all the time
which make them important to learn.

as for templating html, there are many choices. i, of course, recommend
Template::Simple which you can learn quickly and will help in this
task. regardless of the method you need to learn perl data structures.

uri

Point taken! I'm sure even a basic understanding of data structures is a
requirement to use perl for any advanced programming rather than some kind
of rudimentary shell like enhancement tool.

Tuxedo
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,737
Latest member
Georgeengab

Latest Threads

Top