Modyfying loop?

A

Anno Siegel

michael said:
I'm trying to print hash array's by using a loop, whereby each loop
iteration except the first, should become part of a nested <UL>.

The word "nested" is a keyword in programming. It often means that
a recursive approach is in order.

Suppose you know what to do (what string to return) when there are
no nested parts. To find the string for a structure that has nested
parts, find (recursively) the strings for the parts and glue them
together with (usually fixed) other strings.

It may be hard to fix the looping approach (which I snipped), though
with an explicit stack it should be possible. I'm not for recursion
at any cost, but here it may be worth a try.

Anno
 
G

Gunnar Hjalmarsson

michael said:
I'm trying to print hash array's by using a loop, whereby each loop
iteration except the first, should become part of a nested <UL>.

I would consider a different data structure, to begin with. Instead of a
bunch of hashes, an AoAoA might be suitable:

my @AoAoA = (
[
[ 'Flora & Fauna' => 'z0.html' ],
[ 'Pretty Flowers' => 'z1.html' ],
[ 'Deadly Vines' => 'z2.html' ],
],
[
[ 'Aquatic Creatures' => 'z3.html' ],
[ 'Pretty Fish' => 'z4.html' ],
[ 'Don\'t Eat' => 'z5.html' ],
],
);

That would let you keep the order without using Tie::IxHash. The AoAoA
can be printed using nested foreach loops:

print qq(<ul id="menu">\n);
for (@AoAoA) {
my ($label, $url) = @{ shift @$_ };
print qq( <li><a href="$url">$label</a>\n <ul>\n);
for my $pair (@$_) {
my ($label, $url) = @$pair;
print qq( <li><a href="$url">$label</a></li>\n);
}
print " </ul>\n </li>\n";
}
print "</ul>\n";
 
M

michael

I'm trying to print hash array's by using a loop, whereby each loop
iteration except the first, should become part of a nested <UL>.
Firstly, this prints just one <UL> group of <LI>'s of 2 hash arrays:

use Tie::IxHash;

tie %flora_and_fauna, "Tie::IxHash";
tie %aquatic_creatures, "Tie::IxHash";

%flora_and_fauna = ('Flora & Fauna' => 'z0.html',
'Pretty Flowers' => 'z1.html',
'Deadly Vines' => 'z2.html');

%aquatic_creatures = ('Aquatic Creatures' => 'z3.html',
'Pretty Fish' => 'z4.html',
'Don\'t Eat' => 'z5.html');

print "<ul id=menu><!-- opened holding UL -->\n";

foreach $key ( keys %flora_and_fauna) {
$value = $flora_and_fauna{$key};
print "<li>";
print "<a href=$value>$key</a>";
print "</li>\n";
}

foreach $key ( keys %aquatic_creatures) {
$value = $aquatic_creatures{$key};
print "<li>";
print "<a href=$value>$key</a>";
print "</li>\n";
}

print "</ul><!-- closed holding UL -->\n";

Resulting html is then:

<ul id=menu><!-- opened holding UL -->
<li><a href=z0.html>Flora & Fauna</a></li>
<li><a href=z1.html>Pretty Flowers</a></li>
<li><a href=z2.html>Deadly Vines</a></li>
<li><a href=z3.html>Aquatic Creatures</a></li>
<li><a href=z4.html>Pretty Fish</a></li>
<li><a href=z5.html>Don't Eat</a></li>
</ul><!-- closed holding UL -->

Whilst what I would like is the following and slightly different structure:

<ul id=menu><!-- opened holding UL -->
<li><a href=z0.html>Flora & Fauna</a><!-- CUT -->
<UL><!-- open UL here -->
<li><a href=z1.html>Pretty Flowers</a></li>
<li><a href=z2.html>Deadly Vines</a></li>
</UL><!-- close UL here -->
</LI><!-- close LI here -->
<li><a href=z3.html>Aquatic Creatures</a><!-- CUT -->
<UL><!-- open UL here -->
<li><a href=z4.html>Pretty Fish</a></li>
<li><a href=z5.html>Don't Eat</a></li>
</UL><!-- close UL here -->
</LI><!-- close LI here -->
</ul><!-- closed holding UL -->

So basically I'd like to:

1) Cut the closing </LI> out of the first iteration of each of the loops,
being the last five characters.
2) Print an opening <UL> between first and second iteration only or at the
beginning of the second.
3) Close the </UL> after the last </li> of each hash array (as done below).
4) Insert a closing a </LI> after each hash array (as also done below).

So the following takes care of step 3 and 4 but not 1 and 2 ...

print "<ul id=menu><!-- opened holding UL -->\n";

foreach $key ( keys %flora_and_fauna) {
$value = $flora_and_fauna{$key};
print "<li>";
print "<a href=$value>$key</a>";
print "</li>\n";
}

print "</UL><!-- close UL here -->\n";
print "</LI><!-- close LI here -->\n";

foreach $key ( keys %aquatic_creatures) {
$value = $aquatic_creatures{$key};
print "<li>";
print "<a href=$value>$key</a>";
print "</li>\n";
}

print "</UL><!-- close UL here -->\n";
print "</LI><!-- close LI here -->\n";

print "</ul><!-- closed holding UL -->\n";

.... and that of course prints a wrong html syntax, as below:

<ul id=menu><!-- opened holding UL -->
<li><a href=z0.html>Flora & Fauna</a></li>
<li><a href=z1.html>Pretty Flowers</a></li>
<li><a href=z2.html>Deadly Vines</a></li>
</UL><!-- close UL here -->
</LI><!-- close LI here -->
<li><a href=z3.html>Aquatic Creatures</a></li>
<li><a href=z4.html>Pretty Fish</a></li>
<li><a href=z5.html>Don't Eat</a></li>
</UL><!-- close UL here -->
</LI><!-- close LI here -->
</ul><!-- closed holding UL -->

In other words, I'd simple like to cut one closing </li> and add an opening
<ul> to the above, which may sound easy enough, but it probably isn't...

Could it perhaps be done by modifying the foreach loop, or by copying
values to something else and change with regex or insert/remove by some
other method, or by using a different loop method altogether, not foreach?

Many thanks,
Michael
 
F

Fabian Pilkowski

* michael said:
I'm trying to print hash array's by using a loop, whereby each loop
iteration except the first, should become part of a nested <UL>.
Firstly, this prints just one <UL> group of <LI>'s of 2 hash arrays:

use Tie::IxHash;

tie %flora_and_fauna, "Tie::IxHash";
tie %aquatic_creatures, "Tie::IxHash";

Always, you should use "strict" and "warnings", do you? Please declare
your vars with my(), e.g.

tie my %flora_and_fauna, "Tie::IxHash";
tie my %aquatic_creatures, "Tie::IxHash";
%flora_and_fauna = ('Flora & Fauna' => 'z0.html',
'Pretty Flowers' => 'z1.html',
'Deadly Vines' => 'z2.html');

%aquatic_creatures = ('Aquatic Creatures' => 'z3.html',
'Pretty Fish' => 'z4.html',
'Don\'t Eat' => 'z5.html');
[...]

Whilst what I would like is the following and slightly different structure:

<ul id=menu><!-- opened holding UL -->
<li><a href=z0.html>Flora & Fauna</a><!-- CUT -->
<UL><!-- open UL here -->
<li><a href=z1.html>Pretty Flowers</a></li>
<li><a href=z2.html>Deadly Vines</a></li>
</UL><!-- close UL here -->
</LI><!-- close LI here -->
<li><a href=z3.html>Aquatic Creatures</a><!-- CUT -->
<UL><!-- open UL here -->
<li><a href=z4.html>Pretty Fish</a></li>
<li><a href=z5.html>Don't Eat</a></li>
</UL><!-- close UL here -->
</LI><!-- close LI here -->
</ul><!-- closed holding UL -->

Please indent your lines somehow. No one could read this without effort.
Afterwards, you want to read `perldoc -f each` and try something like


print "<ul id='menu'>\n";
for ( \%flora_and_fauna, \%aquatic_creatures ) {
my $firstkey = each %$_;
print " <li>$firstkey\n";
print " <ul>\n";
print " <li>$_</li>\n" while local $_ = each %$_;
print " </ul>\n";
print " </li>\n";
}
print "</ul>\n";


to get something like

<ul id='menu'>
<li>Flora & Fauna
<ul>
<li>Pretty Flowers</li>
<li>Deadly Vines</li>
</ul>
</li>
<li>Aquatic Creatures
<ul>
<li>Pretty Fish</li>
<li>Don't Eat</li>
</ul>
</li>
Could it perhaps be done by modifying the foreach loop, or by copying
values to something else and change with regex or insert/remove by some
other method, or by using a different loop method altogether, not foreach?

Right, just use a "different loop method". I suggest to use *while and
each* instead of *foreach and keys*.

regards,
fabian
 
M

michael

Always, you should use "strict" and "warnings", do you? Please declare
your vars with my(), e.g.

From now on I do.
tie my %flora_and_fauna, "Tie::IxHash";
tie my %aquatic_creatures, "Tie::IxHash";


%flora_and_fauna = ('Flora & Fauna' => 'z0.html',
'Pretty Flowers' => 'z1.html',
'Deadly Vines' => 'z2.html');

%aquatic_creatures = ('Aquatic Creatures' => 'z3.html',
'Pretty Fish' => 'z4.html',
'Don\'t Eat' => 'z5.html');
[...]


print "<ul id='menu'>\n";
for ( \%flora_and_fauna, \%aquatic_creatures ) {
my $firstkey = each %$_;
print " <li>$firstkey\n";
print " <ul>\n";
print " <li>$_</li>\n" while local $_ = each %$_;
print " </ul>\n";
print " </li>\n";
}
print "</ul>\n";

Thanks for posting this loop, it returns exactly the structure I want,
regardless of how many link items or hash arrays are added to the menu
system.

Only, and what is probably an easy question; how can the <a href... $value's
be included in their right places, so the loop would print the following?

<ul id='menu'>
<li><a href=z0.html>Flora & Fauna</a>
<ul>
<li><a href=z1.html>Pretty Flowers</a></li>
<li><a href=z2.html>Deadly Vines</a></li>
</ul>
</li>
<li><a href=z3.html>Aquatic Creatures</a>
<ul>
<li><a href=z4.html>Pretty Fish</a></li>
<li><a href=z5.html>Don't Eat</a></li>
</ul>
</li>
</ul>

Thanks,
Michael
 
J

Joe Smith

michael said:
Only, and what is probably an easy question; how can the <a href... $value's
be included in their right places, so the loop would print the following?

It is trivial for ordinary hashes; just print $hash{$key} at the right
spot. For the method Fabian described, look at the perl documentation
for 'each'. It returns two scalars.

my ($key,$val) = each %$_;
print qq(<li><a href="$val">$key</a>\n);
print qq( <ul>\n);
print qq( <li><a href="val">$key</a></li>\n)
while ($key,$val) = each %$_;
print qq( </ul>\n);
print qq(</li>\n);

-Joe
 
F

Fabian Pilkowski

* michael said:
Thanks for posting this loop, it returns exactly the structure I want,
regardless of how many link items or hash arrays are added to the menu
system.

Only, and what is probably an easy question; how can the <a href... $value's
be included in their right places, so the loop would print the following?

I know that this was your intention. I've left this as an exercise for
you since it is really "an easy question". Nevertheless, Joe has already
shown one alternative.

regards,
fabian
 
M

michael

my @AoAoA = (
[
[ 'Flora & Fauna' => 'z0.html' ],
[ 'Pretty Flowers' => 'z1.html' ],
[ 'Deadly Vines' => 'z2.html' ],
],
[
[ 'Aquatic Creatures' => 'z3.html' ],
[ 'Pretty Fish' => 'z4.html' ],
[ 'Don\'t Eat' => 'z5.html' ],
],
);

That would let you keep the order without using Tie::IxHash. The AoAoA
can be printed using nested foreach loops:

print qq(<ul id="menu">\n);
for (@AoAoA) {
my ($label, $url) = @{ shift @$_ };
print qq( <li><a href="$url">$label</a>\n <ul>\n);
for my $pair (@$_) {
my ($label, $url) = @$pair;
print qq( <li><a href="$url">$label</a></li>\n);
}
print " </ul>\n </li>\n";
}
print "</ul>\n";

The above moduleless solution works nicely, as does the version posted in
the next thread.

Ultimately, I'm looking for a way to print each <li> also to include 1 of 3
css/html class variables, to be used for visual html formatting, eg.:

<li class=current_url>
<li class=this_group>
<li class=all_others>

To know which should be which, all I can go by is the current page name
being a variable imported into the perl script through
$ENV{"DOCUMENT_NAME"}. Note that the menu would be ssi/perl generated onto
otherwise static .html pages, which are the pages as linked in the arrays.

Through knowing the file name, it should be possible to know which array
group it is within, and for it, print a relevant <li class=$variable>.

For example, if a user is accessing z1.html, I would like to print <li>'s
for z0.html and z2.html, as <li class=current_group>, as z1.html is amongst
that group of links.

Whilst the <li> containing z1.html itself would print as <li
class=current_url>.

And all others, being z3.html, z4.html and z5.html, to print as <li
class=all_others>

Could the above AoAoA/loop format be extended for this, or would a different
array/loop method be needed considering this last mentioned functionality?

Thanks again,
Michael

--
Q: How does the Polish Constitution differ from the American?
A: Under the Polish Constitution citizens are guaranteed freedom of
speech, but under the United States constitution they are
guaranteed freedom after speech.
-- being told in Poland, 1987
 
M

michael

you since it is really "an easy question". Nevertheless, Joe has already
shown one alternative.

I appreciate your ul reconstruct loop. I "just" managed to get it working
including the hrefs, although not as optimal as designed, but it does work:

print "<ul id='menu'>\n";
for (\%flora_and_fauna) {
my $firstkey = each %$_;
print "<li><a href=$flora_and_fauna{$firstkey}>$firstkey</a>\n";
print "<ul>\n";
print "<li><a href=$flora_and_fauna{$_}>$_</li> \n" while local $_ = each
%$_;
print "<ul>\n";
print "</li>\n";
}

for (\%aquatic_creatures) {
my $firstkey = each %$_;
print "<li><a href=$aquatic_creatures{$firstkey}>$firstkey</a>\n";
print "<ul>\n";
print "<li><a href=$aquatic_creatures{$_}>$_</li> \n" while local $_ = each
%$_;
print "<ul>\n";
print "</li>\n";
}

print "</ul>\n";

It may be easier for me to keep each link group as separate loops as there
won't be that many anyway, and as I need to get some different <li
class="$variables"> set for each depending on which page is accessed.

Thanks,
Michael
 
C

Charles DeRykus

I appreciate your ul reconstruct loop. I "just" managed to get it working
including the hrefs, although not as optimal as designed, but it does work:

print "<ul id='menu'>\n";
for (\%flora_and_fauna) {
my $firstkey = each %$_;
print "<li><a href=$flora_and_fauna{$firstkey}>$firstkey</a>\n";
print "<ul>\n";
print "<li><a href=$flora_and_fauna{$_}>$_</li> \n" while local $_ = each
%$_;
print "<ul>\n";
print "</li>\n";
}

for (\%aquatic_creatures) {
my $firstkey = each %$_;
print "<li><a href=$aquatic_creatures{$firstkey}>$firstkey</a>\n";
print "<ul>\n";
print "<li><a href=$aquatic_creatures{$_}>$_</li> \n" while local $_ = each
%$_;
print "<ul>\n";
print "</li>\n";
}

print "</ul>\n";

It may be easier for me to keep each link group as separate loops as there
won't be that many anyway, and as I need to get some different <li
class="$variables"> set for each depending on which page is accessed.


Also, you might want to consider CGI to handle messy tags.
Here's an example using one of your earlier code variants:

use CGI::pretty qw/ :standard li *li *ul /;

print start_ul( {id=>'menu'} );
for ( \%flora_and_fauna, \%aquatic_creatures ) {
my $firstkey = each %$_;
print start_li(), $firstkey, start_ul(),
do { my @l; push(@l,li($_)) while local $_ = each %$_ ;
@l },
end_ul(), end_li();
}
print end_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
474,170
Messages
2,570,925
Members
47,468
Latest member
Fannie44U3

Latest Threads

Top