Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Perl > Perl Misc > Ideal data structure for nested list format?

Reply
Thread Tools

Ideal data structure for nested list format?

 
 
Tuxedo
Guest
Posts: n/a
 
      09-04-2010
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
 
Reply With Quote
 
 
 
 
Tuxedo
Guest
Posts: n/a
 
      09-04-2010
Ben Morrow wrote:

[...]

Many thanks for the detailed concept in how to build the particular menu
and list format. I understand it's not entirely ready-to-run code, and so
I'd like to know how your example can generate the final html code. Perhaps
you or someone here can help fill in the missing parts?:

#!/usr/bin/perl -w

use warnings;
use strict;

my @pages = (
{ page => "1.1.html", title => "Level 1.1", children => [
{ page => "2.1.html", title => "Level 2.1" },
{ page => "2.2.html", title => "Level 2.2", children => [
{ page => "3.1.html", title => "Level 3.1" },
{ page => "3.2.html", title => "Level 3.2" },
] },
{ page => "2.3.html", title => "Level 2.3" },
] },
);

# and then build a hash of 'parents', like so

sub populate_parents;
sub populate_parents {
my ($parents, $pages, $parent) = @_;

for (@$pages) {
my $page = $_->{page};
push @{$parents->{$page}}, $parent;
my $kids = $_->{children};
$kids and populate_parents $parents, @$kids, $page;
}
}

my %parents;
populate_parents \%parents, \@pages, undef;

# This will give you a data structure like

%parents = (
"1.1.html" => [undef],
"2.1.html" => [undef, "1.1.html"],
"3.1.html" => [undef, "1.1.html", "2.2.html"],
# ...
);

# Then, given a page name, you can build another hash

my $page = "3.1.html";
my $parents = $parents{$page};
my %class =
map +($parents->[$_] => "parent_level_$_"),
grep defined $parents->[$_],
0..$#$parents;

The above code 'as is' returns the error "Not an ARRAY reference at
../menu.pl line 23", which is on the line beginning with "for (@$pages) ".

If only the arrays and hashes could be generated without error, I can then
try to access and print the entries through some kind of loop construct.
Any additional ideas and code bits would be greatly appreciated!

Thanks again,
Tuxedo

 
Reply With Quote
 
 
 
 
Uri Guttman
Guest
Posts: n/a
 
      09-04-2010
>>>>> "T" == Tuxedo <(E-Mail Removed)> writes:

T> Ben Morrow wrote:
T> [...]

T> Many thanks for the detailed concept in how to build the particular menu
T> and list format. I understand it's not entirely ready-to-run code, and so
T> I'd like to know how your example can generate the final html code. Perhaps
T> you or someone here can help fill in the missing parts?:

T> #!/usr/bin/perl -w

T> use warnings;
T> use strict;

T> my @pages = (
T> { page => "1.1.html", title => "Level 1.1", children => [
T> { page => "2.1.html", title => "Level 2.1" },
T> { page => "2.2.html", title => "Level 2.2", children => [
T> { page => "3.1.html", title => "Level 3.1" },
T> { page => "3.2.html", title => "Level 3.2" },
T> ] },
T> { page => "2.3.html", title => "Level 2.3" },
T> ] },
T> );

gack!! use a templater. many to choose from. check out mine called
Template::Simple. you pass it a data structure and template and you get
rendered text. nothing more to it. you can nest structures, templates
and template chunks all you want. lists are automatic too with no extra
markup (a list of elements renders a list of template chunks). all you
need to do is create your html templates (again, they can be included
and nested) and render with one call.

uri

--
Uri Guttman ------ http://www.velocityreviews.com/forums/(E-Mail Removed) -------- http://www.sysarch.com --
----- Perl Code Review , Architecture, Development, Training, Support ------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
 
Reply With Quote
 
Tuxedo
Guest
Posts: n/a
 
      09-04-2010
Uri Guttman wrote:

[...]

> gack!! use a templater. many to choose from. check out mine called
> Template::Simple. you pass it a data structure and template and you get
> rendered text. nothing more to it. you can nest structures, templates
> and template chunks all you want. lists are automatic too with no extra
> markup (a list of elements renders a list of template chunks). all you
> need to do is create your html templates (again, they can be included
> and nested) and render with one call.
>
> uri
>


Thanks for the tip, I've not tried Template::Simple before so I'm not sure
how it works yet. I need to install and test it as well as delve into the
manual, which looks a little less than simple at first sight...

Can anyone advise me how to generate the following type of nested list by
Template::Simple?

<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>

And is it actually possible to apply the advanced formatting relating to
the $current_page URL along with any parent <li class=bits> if and where
parent entry <li>'s are present? (as detailed in my first post in this
thread). Can all of this be done with the help of Template::Simple?

Many thanks,
Tuxedo
 
Reply With Quote
 
Uri Guttman
Guest
Posts: n/a
 
      09-05-2010
>>>>> "T" == Tuxedo <(E-Mail Removed)> writes:

T> Thanks for the tip, I've not tried Template::Simple before so I'm
T> not sure how it works yet. I need to install and test it as well as
T> delve into the manual, which looks a little less than simple at
T> first sight...

it has 4 markups. 150 lines of total code. simple is as simple codes!

the docs are fairly clear (as clear as i can write them!). can you ask
specific questions?

T> Can anyone advise me how to generate the following type of nested list by
T> Template::Simple?

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

not much different than with any other templater. first break it up into
'chunks' as i call them. each ul/li level and possibly each data row
would be a chunk. a template chunk maps to a perl hash. lists are just
generated by passing an array of hashes instead of a single hash. note
that you can skip a chunk by not stuffing it or passing an empty hash to
it. that is how you do conditionals

so the inner levels would be something like this (VERY untested as i am
typing on the fly):

[% START unordered %]
<ul>
[% START link_line %]
<li><a href=[%level%].html>Level [%level%]</a></li>
[% END link_line %]
</ul>
[% END unordered %]

that is the basic template. recursion to make any depth is not directly
supported but you can fake it easily with a code ref in the data or
doing subtemplates yourself.

save that in a file and you can apply data to it and render it. if you
break up your data with code refs at different levels, each code ref can
render its level using the same template. a little more complex but
doable. the code refs are best if they are closures. they get passed the
template and they should have their data as closure args. i am not going
to teach that now.

so the basic data would look like this for the inner levels:

{
unordered => {
link_line => [
{ level => '3.1' },
{ level => '3.2' },
]
}
}


as you can see the data matches the tree shape of the template. when i
get some time i may show how to do deeper recursion with closures. just
not tonight.

T> And is it actually possible to apply the advanced formatting relating to
T> the $current_page URL along with any parent <li class=bits> if and where
T> parent entry <li>'s are present? (as detailed in my first post in this
T> thread). Can all of this be done with the help of Template::Simple?

didn't follow that so i can't help.

uri

--
Uri Guttman ------ (E-Mail Removed) -------- http://www.sysarch.com --
----- Perl Code Review , Architecture, Development, Training, Support ------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
 
Reply With Quote
 
Peter J. Holzer
Guest
Posts: n/a
 
      09-05-2010
On 2010-09-04 22:44, Uri Guttman <(E-Mail Removed)> wrote:
>>>>>> "T" == Tuxedo <(E-Mail Removed)> writes:

>
> T> Ben Morrow wrote:
> T> [...]
>
> T> Many thanks for the detailed concept in how to build the particular menu
> T> and list format. I understand it's not entirely ready-to-run code, and so
> T> I'd like to know how your example can generate the final html code. Perhaps
> T> you or someone here can help fill in the missing parts?:
>
> T> #!/usr/bin/perl -w
>
> T> use warnings;
> T> use strict;
>
> T> my @pages = (
> T> { page => "1.1.html", title => "Level 1.1", children => [
> T> { page => "2.1.html", title => "Level 2.1" },
> T> { page => "2.2.html", title => "Level 2.2", children => [
> T> { page => "3.1.html", title => "Level 3.1" },
> T> { page => "3.2.html", title => "Level 3.2" },
> T> ] },
> T> { page => "2.3.html", title => "Level 2.3" },
> T> ] },
> T> );
>
> gack!! use a templater.


With a templater you still need to represent the data you want to render
with the templater. I don't see how you could represent a tree, where
each node contains a link to a page and a title, much differently in
perl than Ben showed above (I am assuming that "page" and "title" aren't
as redundant as the seem to be in the example. Instead of "Level 1.1"
the title would probably be something like "1.1 - Introduction" in the
real world).

> many to choose from. check out mine called Template::Simple. you pass
> it a data structure


Yup. But you have to have the data structure first. That's what you
quoted above.

hp

 
Reply With Quote
 
Tuxedo
Guest
Posts: n/a
 
      09-05-2010
Ben Morrow wrote:

[...]

> That would be because I'm an idiot, and was typing in the code without
> testing it, and made an elementary mistake. However, I'm going to leave
> it to you to find it, because you need to understand the code at least
> that well before you use it. (I'll give you a hint: $pages isn't an
> array reference, and it ought to be.)
>
> Ben
>


Thank for the hint. Anyway, why should you test the code? After all, you've
already offered some most helpful advise along with even purpose written
code. Nobody expects it to work without errors, although it would be nice
if it did. That said, I'm the better runner-up for the idiot title in not
seeing through your code properly. However, once I know it may produce the
relevant list order and particular variable output, I would surely study it
character-by-character until I do know its intricate functionality.

On the line where the error is returned it says @$pages. As far as I
understand an array reference is written \@pages

So I changed @$pages to \@pages as I guess it should be. The same error no
longer occurs but instead there are some other errors:

tuxedo:~$ ./menu.pl
Pseudo-hashes are deprecated at ./menu.pl line 24.
Argument "1.1.html" isn't numeric in hash element at ./menu.pl line 24.
Use of uninitialized value in hash element at ./menu.pl line 25.
Pseudo-hashes are deprecated at ./menu.pl line 26.

These are lines 24/25/26 of code :

my $page = $_->{page};
push @{$parents->{$page}}, $parent;
my $kids = $_->{children};

Again, I do not see through these particular errors and so any further
hints from anyone here would be greatly appreciated

Tuxedo
 
Reply With Quote
 
Tuxedo
Guest
Posts: n/a
 
      09-05-2010
Uri Guttman wrote:

> >>>>> "T" == Tuxedo <(E-Mail Removed)> writes:

>
> T> Thanks for the tip, I've not tried Template::Simple before so I'm
> T> not sure how it works yet. I need to install and test it as well as
> T> delve into the manual, which looks a little less than simple at
> T> first sight...
>
> it has 4 markups. 150 lines of total code. simple is as simple codes!


I installed Template::Simple as stand-alone module, after installing
File::Slurp and setting a use lib 'external_modules/lib/perl5/site_perl'
call in the Makefile.PL script within the Template-Simple-0.02 directory.

The 'make test' stage of the installation failed with many errors (too many
to list here). Some were 'Can't locate File/Slurp.pm'. Perhaps this was the
cause of all consecutive errors, because of the external module location
and so File::Slurp could not be found by this installation step. Maybe this
is not important? Aftrer all, the other steps: perl Makefile.PL
PREFIX=/mypath/external_modules/, make and make install worked fine, so I
think the module should now be fully functional. When being in the
directory where I ran the installation, I can do 'perldoc Template::Simple'.

PS: I install modules in external directories because I usually run into
insufficient system privilidges on various web hosts.

> the docs are fairly clear (as clear as i can write them!). can you ask
> specific questions?


Can someone here place the list example in an working working script? Are
separate parts meant to exist in separate files? Alternatively, I think I
need an 'hello, I'm a template' equivalent 'hello world'... Anyone?

[...]

> so the inner levels would be something like this (VERY untested as i am
> typing on the fly):
>
> [% START unordered %]
> <ul>
> [% START link_line %]
> <li><a href=[%level%].html>Level [%level%]</a></li>
> [% END link_line %]
> </ul>
> [% END unordered %]


[...]

> so the basic data would look like this for the inner levels:
>
> {
> unordered => {
> link_line => [
> { level => '3.1' },
> { level => '3.2' },
> ]
> }
> }


[...]

> T> And is it actually possible to apply the advanced formatting relating
> T> to the $current_page URL along with any parent <li class=bits> if and
> T> where parent entry <li>'s are present? (as detailed in my first post
> T> in this thread). Can all of this be done with the help of
> T> Template::Simple?
>
> didn't follow that so i can't help.


The $current_page is a variable output when a page access matches one that
exists in an array field, being the filename portion of the current URL.
The $current_page is known to any perl process while run through a CGI
process, which is importing the link list onto otherwise static HTML pages.
To clarify by an example, if you are at a page (one you happen to know)
such as http://bestfriendscocoa.com/recipes.html then 'Tasty Recipes' in
the header would not show as what may appear to some people as a link to a
different page, but would instead be printed in plain text.

Any other formatting I refer to is applicable in a nested navigation system
of the unordered list I posted previously, typically used by many CSS
navbars. For example, if users are on page 3.2.html, the parent <li> which
contains the link 2.2.html, would be formatted with a style class <li
class=parent_level_2>, while the parent of that, being the <li> containing
the link 1.1.html would be formatted with an <li class=parent_level_1>.
This allows for building a clear navigation system. I guess
Template::Simple can do all this, but maybe not all as any kind of standard
function. After all, the idea of the list output is actually very simple.

Tuxedo

 
Reply With Quote
 
Uri Guttman
Guest
Posts: n/a
 
      09-06-2010
>>>>> "BM" == Ben Morrow <(E-Mail Removed)> writes:

BM> Using T::S you would need to go through and insert class => "..."
BM> entries in your main data structure. There isn't any way to use a
BM> subsidiary hash the way I was suggesting. (This is one of the things I
BM> find slightly irritating about T::S, though I've yet to find a better
BM> alternative for basic (i.e., 'don't want TT2') stuff.)

how would you modify T::S to accomodate your idea? remember, my goal was
a very simple and fast templater. you can do very fancy stuff with code refs
in the data tree. the first public version was 37 lines of code. that
morphed into 150 lines when i made it OO and a proper module. that is
the definition of simple!

uri

--
Uri Guttman ------ (E-Mail Removed) -------- http://www.sysarch.com --
----- Perl Code Review , Architecture, Development, Training, Support ------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
 
Reply With Quote
 
Tuxedo
Guest
Posts: n/a
 
      09-06-2010
Ben Morrow wrote:

>
> Quoth Tuxedo <(E-Mail Removed)>:
> > Uri Guttman wrote:
> >
> > > >>>>> "T" == Tuxedo <(E-Mail Removed)> writes:


[...]

> > I installed Template::Simple as stand-alone module, after installing
> > File::Slurp and setting a use lib 'external_modules/lib/perl5/site_perl'
> > call in the Makefile.PL script within the Template-Simple-0.02
> > directory.

>
> Why did you do that? You shouldn't be editing the Makefile.PL at all.
> Put external_module/.../site_perl in PERL5LIB in the environment,
> instead.


Is PERL5LIB meant to be a temporary Unix environment variable at time of
installation, pointing to /some/external_module/.../site_perl? Eg.:
PERL5LIB=/path/to/module_dir/
Does Makefile.PL probe for that during module installation, to find any
relevant dependency modules that may not otherwise exist in Perl's standard
library locations? Or do you mean something else regarding PERL5LIB?

[...]

> If you haven't run the tests you have no reason for thinking that.


I'm not sure what you mean, I ran 'make test' and there were many errors.

[...]

> my $T = Template::Simple->new;
> my $ref = $T->render(<<T, { world => "I'm a template" });
> Hello, [% world %]!
> T
> print $$ref;


Thanks for this bit of code, I'm sure it works

[...]

> Using T::S you would need to go through and insert class => "..."
> entries in your main data structure. There isn't any way to use a
> subsidiary hash the way I was suggesting. (This is one of the things I
> find slightly irritating about T::S, though I've yet to find a better
> alternative for basic (i.e., 'don't want TT2') stuff.)


Thanks for this advise as well. It sounds highly complex to make T::S do
what I'd like for this particular, yet simple, nested list structure.

Tuxedo

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Iterate through a list of structure arrays of structure to get outthe field stuie_norris@yahoo.com.au C Programming 2 12-12-2012 09:18 PM
trouble with STL list initialization inside nested structure M A C++ 4 09-30-2009 07:14 AM
Accessing complex data from a linked list based data structure.. Ram C Programming 3 03-24-2009 02:15 AM
selection structure nested within another selection structure 4Ankit@gmail.com Javascript 1 12-07-2006 11:47 AM
QUESTION: Good data structure for list within list with duplicates? basi Ruby 4 08-01-2005 07:30 PM



Advertisments