Ideal data structure for nested list format?

U

Uri Guttman

JE> Then that is your problem. A plumber needs to know how to use a blow
JE> torch and a programmer needs to know how to use recursive data
JE> structures. Neither is rocket science, but without the knowledge you
JE> won't get far in either area, not even as a dabbling amateur.

i thought coders need to know how to use a blowtorch as well!

uri
 
T

Tuxedo

Sherm said:
[..]

That's a *very* poor occasion on which to attempt a learning exercise.
When deadlines are tight and looming, stick with the tools you know.

True enough! I found a simple way to produce what is needed by importing an
html list from a text file and replacing the relevant parts by regex based
on the current URL (filename) in a series of if-blocks and returning the
modified output, which is nearly like typing each html page out separately:

--------- menu.pl -------

#!/usr/bin/perl -w

use warnings;
use strict;

# used for CGI
$this::page = $ENV{DOCUMENT_NAME} || 'undefined';

# test on command line, eg. 'menu.pl page_2.2.html'
if ($this::page eq 'undefined'){
$this::page = "@ARGV";
}

print "Content-Type: text/html\n\n";

my $filename = "menu.txt" ;
my $filedata ;

open my $fh, '<', $filename
or die "open $filename: $!" ;

{
local $/ ;
$filedata = <$fh> ;
}
close $fh
or die "close $filename: $!" ;

$_ = $filedata; # assign filedata to $_
my $complete_string = $filedata;

if ($this::page eq "page_1.1.html"){
$complete_string =~ s/<a
href="page_1.1.html">page_1.1.html<\/a>/page_1.1.html/;
}

if ($this::page eq "page_2.1.html"){
$complete_string =~ s/<a
href="page_2.1.html">page_2.1.html<\/a>/page_2.1.html/;
$complete_string =~ s/page_1.1.html"/page_1.1.html" class="node"/;
}

if ($this::page eq "page_2.2.html"){
$complete_string =~ s/<a
href="page_2.2.html">page_2.2.html<\/a>/page_2.2.html/;
$complete_string =~ s/page_1.1.html"/page_1.1.html" class="node"/;
}

if ($this::page eq "page_3.1.html"){
$complete_string =~ s/<a
href="page_3.1.html">page_3.1.html<\/a>/page_3.1.html/;
$complete_string =~ s/page_1.1.html"/page_1.1.html" class="node"/;
$complete_string =~ s/page_2.2.html"/page_2.2.html" class="node"/;
}

if ($this::page eq "page_3.2.html"){
$complete_string =~ s/<a
href="page_3.2.html">page_3.2.html<\/a>/page_3.2.html/;
$complete_string =~ s/page_2.2.html"/page_2.2.html" class="node"/;
$complete_string =~ s/page_1.1.html"/page_1.1.html" class="node"/;
}

if ($this::page eq "page_2.3.html"){
$complete_string =~ s/<a
href="page_2.3.html">page_2.3.html<\/a>/page_2.3.html/;
$complete_string =~ s/page_1.1.html"/page_1.1.html" class="node"/;

}

print $complete_string;

------

The unmodified list in menu.txt looks as follows:

<style type="text/css">
..node {
font-style: italic;
}
</style>

<ul>
<li><a href="page_1.1.html">page_1.1.html</a>
<ul>
<li><a href="page_2.1.html">page_2.1.html</a></li>
<li><a href="page_2.2.html">page_2.2.html</a>
<ul>
<li><a href="page_3.1.html">page_3.1.html</a></li>
<li><a href="page_3.2.html">page_3.2.html</a></li>
</ul>
</li>
<li><a href="page_2.3.html">page_2.3.html</a></li>
</ul>
</li>
</ul>

So the example perl code is for only six pages, just imagine what it will
look like with 50 or more pages and what kind of hassle that can be to
maintain. In other words, the code could surely win the ugliest perl script
posted here contest. That said, it actually does what is needed in a
roundabout way and given the facts that changes won't be very frequent and
that it's only an html-preprocessing procedure, since static versions will
be served online, it doesn't need to be easy to maintain or even system
resource friendly. In other words, that the code is bloated and a bit hard
to manage won't affect the end result. Nevertheless, this is my primitive
solution by the tools I happen to know, which all they do is to produce
some modified output based on the current URL (if one is matching) and
adding the css html class to some nodes at the right levels for each link.

Any ideas how to improve the code are of course more than welcome, although
I guess the only *real* improvement would mean a nested data structure,
recursion and complex references etc., although that may be easy to most of
you here, I must avoid that for now due to my inability to bugfix such code.

Thanks for any feedback!

Tuxedo
 
D

Dr.Ruud

my $filedata = do {
open my $FH, "<", "...";
local $/;
<$FH>;
};

Alternative:

my $filedata;
{ open my $fh, "<", "..." or die $!;
local $/;
$filedata = <$fh>;
}

which uses less memory.
 
S

sln

In trying to construct a nested html list with perl I have some questions
what the ideal data structure may be for the specific purpose.

The menu is generated on the fly for each page request. The script lives in
a perl file named menu.pl and the current page that is accessed is known to
the script via a server environment variable accessible in a $current_page
perl variable. The html parts are imported onto individual html pages
through SSI (includes) and all pages are placed at a root level, so there
are no sub-directories or chances of any pages having a same name.

This is a simplified html output of the menu in an unordered list format:

<ul>
<li><a href=1.1.html>Level 1.1</a>
<ul>
<li><a href=2.1.html>Level 2.1</a></li>
<li><a href=2.2.html>Level 2.2</a>
<ul>
<li><a href=3.1.html>Level 3.1</a></li>
<li><a href=3.2.html>Level 3.2</a></li>
</ul>
</li>
<li><a href=2.3.html>Level 2.3</a></li>
</ul>
</li>
</ul>

So there are three levels and the first two levels have nested lists
within. If for example page 3.2.html is accessed, the script should write
out that <li> entry *without* the enclosing <a href=3.2.html>..</a> parts,
as well as add a css-class to that one list item: <li class=current_page>.

The script should know its nearest parent <li> item, so in case of
accessing page 3.2.html, that would be the <li> with the 2.2.html link.
This entry should then have have the css-class: <li class=parent_level_2>.

Additionally, the second nearest parent should be given another css-class,
such as <li class=parent_level_1> (so it may be styled differently).

If the current page is one that does not exist in the menu, although the
menu has been imported on such a page, then no special classes or non-link
formatting is needed for any of the array entries. The html output would
just appear exactly as the above example.

Without the actual link names, I guess the list may be constructed with a
LoL or an AoA, such as:

my @AoA = ('1.1.html',
['2.1.html',
'2.2.html',
['3.1.html',
'3.2.html'],
'2.3.html']);

If so, a parallel AoA's would be needed for the link names to be combined
in a final loop. Or is some other data structure better suited for the
purpose? I.e.: sticking all items together in a nested html list in order
of entry in the perl code, as well as figure out the opening parent <li>
points (if any) in order to apply custom css-classes to relevant <li>'s.

Looking at perldsc, in addition to AoA, alternatives such as HoA, AoH, HoH
as well as some more elaborate data structures exist. I'm not sure what's
best? The maintenance of the link list will involve changing or updating
entries only very occasionally, so it can be done in separate arrays in
case that's better. Anyway, it's not important how, as long as it works...

What is the ideal data structure for the particular hierarchial list
functionality and where the described level specific output can easily be
incorporated?

Many thanks for any advise or general pointers.

Tuxedo

The rabit hole goes very deep.

-sln

---------------
use strict;
use warnings;

##
my $template1 = <<LIST1;
[
('1.1.html',Level 1.1
[
('2.1.html',Level 2.1)
('2.2.html',Level 2.2)
[
('3.1.html',Level 3.1)
('3.2.html',Level 3.2)
]
('2.3.html',Level 2.3)
]
)
]
LIST1

##
my $regex = qr/
( #1
\(
( #2
(?:
(?> [^()]+ )
| (?1)
)*
)
\)
)
|
( #3
\[
( #4
(?:
(?> [^\[\]]+ )
| (?3)
)*
)
\]
)
|
'\s* ([^']*)\s*'\s*, ([^<\n]*) #5,6
/xs;

##
my $html = $template1;
1 while ($html =~ s/$regex/
defined $1 ? "<li>$2<\/li>" :
defined $3 ? "<ul>$4<\/ul>" :
"<a href=\"$5\">$6<\/a>"/eg);

print "template ->\n$template1\n";
print "html ->\n$html\n";
__END__
-------------------------

Output:

template ->
[
('1.1.html',Level 1.1
[
('2.1.html',Level 2.1)
('2.2.html',Level 2.2)
[
('3.1.html',Level 3.1)
('3.2.html',Level 3.2)
]
('2.3.html',Level 2.3)
]
)
]

html ->
<ul>
<li><a href="1.1.html">Level 1.1</a>
<ul>
<li><a href="2.1.html">Level 2.1</a></li>
<li><a href="2.2.html">Level 2.2</a></li>
<ul>
<li><a href="3.1.html">Level 3.1</a></li>
<li><a href="3.2.html">Level 3.2</a></li>
</ul>
<li><a href="2.3.html">Level 2.3</a></li>
</ul>
</li>
</ul>
 
T

Tuxedo

(e-mail address removed) wrote:

[...]

Thanks for posting your code! However, when running it I get the following
output:

Sequence (?1...) not recognized in regex; marked by <-- HERE in m/
( #1
\(
( #2
(?:
(?> [^()]+ )
| (?1 <-- HERE )
)*
)
\)
)
|
( #3
\[
( #4
(?:
(?> [^\[\]]+ )
| (?3)
)*
)
\]
)
|
'\s* ([^']*)\s*'\s*, ([^<\n]*) #5,6
/ at ./tmp/temp.pl line 45.

I'm sure your script produced the other output you posted on your own
system. Maybe I have another regex engine in my perl or something. My
system reports my Perl version as 5.008008, which is obviously not the
latest version.

Tuxedo
 
T

Tuxedo

Ben Morrow wrote:

Thanks for checking and taking the time commenting line-by-line!
If this isn't your actual code (you *have* read the Posting Guidelines,
right?) and the title is not necessarily the name of the page, you just
need a regex capture:

I didn't read the gudelines. I guess they state something like example code
should reflect real code as close as possible. So you are right in guessing
that the page name will not be repeated in the title.
$complete_string =~ s[<a href="$this_page">(.*?)</a>][$1]s;

The '?' there is important: it stops the pattern matching everything up
to the final '</a>' in the file.

Thanks for this too and all the other pointers, which helps me learn
something new as well as making the script slightly more manageable, at
least until a better version exists, i.e. one that uses a nested data
structure rather than my flat printing combined with a series of repetitive
regex rewrite calls to simply reformat the output. Yet, it works :)

Tuxedo
 
T

Tuxedo

Sherm said:
No, that's not what the guidelines say. Please read them, so you won't
have to guess next time.

I'll surely get around to that one day...

Tuxedo
 
T

Tuxedo

Sherm said:
One day soon, please. Doing so will be *far* more useful to you than
the snotty attitude you seem to think is appropriate here.

That I say I'll read the guidelines one day, means just that, even in a
positive way. In fact, I'm looking forward to it as soon as I have the
time. However, I also realise the comment could easily be misread in a
negative context, which is obviously how you read it. Personally I don't
assume the negative by default and the snotty attitude you say I *seem* to
think is appropriate here is simply not the case.
Thanks again for your advise, it was well understood the first time.

Tuxedo
 
T

Tuxedo

Tad McClellan wrote:

[...]
Too late.

For exactly what? Your personal plonking level?
Off to perpetual invisibility with you!

Even after I've read your guidelines? Your words could of course be said in
a more simple language, but that may just be considered poor netiquette.

I wish you a nice day.

Tuxedo
 
S

sln

True enough! I found a simple way to produce what is needed by importing an
html list from a text file and replacing the relevant parts by regex based
on the current URL (filename) in a series of if-blocks and returning the
modified output, which is nearly like typing each html page out separately:

--------- menu.pl -------
So the example perl code is for only six pages, just imagine what it will
look like with 50 or more pages and what kind of hassle that can be to
maintain.
Any ideas how to improve the code are of course more than welcome

-sln
--------------
use strict;
use warnings;

##
my $template1 = <<ETMP;
[
('1.1.html',Level 1.1
[
('2.1.html',Level 2.1)
('2.2.html',Level 2.2
[
('3.1.html',Level 3.1)
('3.2.html',Level 3.2)
]
)
('2.3.html',Level 2.3)
]
)
]
ETMP

my $pf = 0;
use re 'eval';

##

print qq{
<style type="text/css">
..node {
font-style: italic;
}
</style>
};

for my $target ('1.1.html',
'2.1.html',
'2.2.html',
'3.1.html',
'3.2.html',
'2.3.html',
'none' )
{
my $html = $template1;
1 while ($html =~ s/
(?:
'\s*$target\s*'\s*, ([^<\n]*) #1
| (parent)?'\s* ([^']*)\s*'\s*, ([^<\n]*) #2,3,4
| ( #5
\(
( #6
(?:
(?> [^()]+ )
| (?=\( '\s*$target\s*' ) (?{ $pf=1 }) (?5)
| (?5)
)*
)
\)
)
|
( #7
\[
( #8
(?:
(?> [^\[\]]+ )
| (?7)
)*
)
\]
)
)
/
my $tmp =
defined $5 ? "<li>". ($pf ? "parent" : "") ."$6<\/li>" :
defined $7 ? "<ul>$8<\/ul>" :
defined $3 ? "<a href=\"$3\"" .
(defined $2 ? " class=\"node\"" : "") .
">$4<\/a>" :
"$1";
$pf = 0;
$tmp
/xeg);

print "\n<!-- $target -->\n\n$html\n";
}

__END__

Output:

<style type="text/css">
..node {
font-style: italic;
}
</style>

<!-- 1.1.html -->

<ul>
<li>Level 1.1
<ul>
<li><a href="2.1.html">Level 2.1</a></li>
<li><a href="2.2.html">Level 2.2</a>
<ul>
<li><a href="3.1.html">Level 3.1</a></li>
<li><a href="3.2.html">Level 3.2</a></li>
</ul>
</li>
<li><a href="2.3.html">Level 2.3</a></li>
</ul>
</li>
</ul>


<!-- 2.1.html -->

<ul>
<li><a href="1.1.html" class="node">Level 1.1</a>
<ul>
<li>Level 2.1</li>
<li><a href="2.2.html">Level 2.2</a>
<ul>
<li><a href="3.1.html">Level 3.1</a></li>
<li><a href="3.2.html">Level 3.2</a></li>
</ul>
</li>
<li><a href="2.3.html">Level 2.3</a></li>
</ul>
</li>
</ul>


<!-- 2.2.html -->

<ul>
<li><a href="1.1.html" class="node">Level 1.1</a>
<ul>
<li><a href="2.1.html">Level 2.1</a></li>
<li>Level 2.2
<ul>
<li><a href="3.1.html">Level 3.1</a></li>
<li><a href="3.2.html">Level 3.2</a></li>
</ul>
</li>
<li><a href="2.3.html">Level 2.3</a></li>
</ul>
</li>
</ul>


<!-- 3.1.html -->

<ul>
<li><a href="1.1.html" class="node">Level 1.1</a>
<ul>
<li><a href="2.1.html">Level 2.1</a></li>
<li><a href="2.2.html" class="node">Level 2.2</a>
<ul>
<li>Level 3.1</li>
<li><a href="3.2.html">Level 3.2</a></li>
</ul>
</li>
<li><a href="2.3.html">Level 2.3</a></li>
</ul>
</li>
</ul>


<!-- 3.2.html -->

<ul>
<li><a href="1.1.html" class="node">Level 1.1</a>
<ul>
<li><a href="2.1.html">Level 2.1</a></li>
<li><a href="2.2.html" class="node">Level 2.2</a>
<ul>
<li><a href="3.1.html">Level 3.1</a></li>
<li>Level 3.2</li>
</ul>
</li>
<li><a href="2.3.html">Level 2.3</a></li>
</ul>
</li>
</ul>


<!-- 2.3.html -->

<ul>
<li><a href="1.1.html" class="node">Level 1.1</a>
<ul>
<li><a href="2.1.html">Level 2.1</a></li>
<li><a href="2.2.html">Level 2.2</a>
<ul>
<li><a href="3.1.html">Level 3.1</a></li>
<li><a href="3.2.html">Level 3.2</a></li>
</ul>
</li>
<li>Level 2.3</li>
</ul>
</li>
</ul>


<!-- none -->

<ul>
<li><a href="1.1.html">Level 1.1</a>
<ul>
<li><a href="2.1.html">Level 2.1</a></li>
<li><a href="2.2.html">Level 2.2</a>
<ul>
<li><a href="3.1.html">Level 3.1</a></li>
<li><a href="3.2.html">Level 3.2</a></li>
</ul>
</li>
<li><a href="2.3.html">Level 2.3</a></li>
</ul>
</li>
</ul>
 

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

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top