#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2013
    Posts
    1
    Rep Power
    0

    Create nested HTML table through perl


    I am creating a hash ref to create a HTML table with parent and child relationship, where key of hash are display order and using a function to create that table but I am unable to get desired output. my hash ref as follows:
    $hash = {
    '3' => {

    'Name' => 'Parent-3',
    },
    '1' => {

    'children' => {
    '11' => {
    'Name' => 'child-1',
    },
    '5' => {

    'children' => {
    '8' => {
    'Name' => 'first child of child-2',
    },
    '13' => {
    'Name' => 'second child of child-2',
    }
    },
    'Name' => 'child-2',
    }
    },
    'Name' => 'Parent-1',
    },
    '2' => {
    'Name' => 'Parent-2',
    },
    '4' => {
    'Name' => 'Parent-4',
    },
    };

    and the recursion function what I am using as follows:
    sub listTree {

    my $hash = shift;
    my $options = '';
    my $iter;
    $options .= "<table id='sort' class='grid' >
    <tbody>";
    $iter = sub {
    my $hash = shift;
    my $indent = shift || '';
    foreach my $k (sort keys %{$hash}) {
    my $v = $hash->{$k};
    $options .= "<tr><td>$v->{Name}" unless $indent;
    if($indent){
    $options .= "<table id='sort'>
    <tbody>
    <tr>
    <td> $indent $v->{Name}</td>
    </tr>
    </tbody>
    </table>";
    }
    if ($v->{children}){
    $iter->($v->{children}, $indent . "--");
    }
    $options .= "</td></tr>" unless $indent;
    }
    };
    $iter->($hash);
    $options .="</tbody></table>";
    return $options;
    }

    print Dumper listTree($hash);

    and it prints the table for each child while the output should be as follows:
    <table id="sort" class="grid" >
    <tbody>
    <tr>
    <td><label>Parent-1</label>
    <table id="sort">
    <tbody>
    <tr>
    <td>-- child-1</td>
    </tr>
    <tr>
    <td><label>-- child-2</label>
    <table id="sort">
    <tbody>
    <tr><td>---- first child of child-2</td></tr>
    <tr>
    <td>---- second child of child-2</td></tr>
    </tbody>
    </table>
    </td>
    </tr>
    </tbody>
    </table>
    </td>
    </tr>

    <tr><td>Parent-2</td></tr>
    <tr><td>Parent-3</td></tr>
    <tr><td>Parent-4</td></tr>
    <tr><td>Parent-5</td></tr>
    </tbody>
    </table>
    I am not able to sort out. please help, its urgent. thanks in advance!
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Apr 2009
    Posts
    1,921
    Rep Power
    1225
    Please use the code tags whenever you post code. It will retain the code formatting and makes it easier to read/follow.

    Why are you nesting your subroutine definitions? And why are you even using an anonymous sub? Neither of those make any sense.

    You really should be using one of the template modules. Doing so would allow you to create the html structure as you see fit and separate from the code logic. All the script would need to do would be to assign some template vars which is then passed to the template.
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Apr 2009
    Posts
    1,921
    Rep Power
    1225
    I should have pointed out that nested tables is the wrong approach. Tables are used for displaying tabular data. A tree structure is not tabular.

    Use nested un-ordered list <ul><li></li></ul> tags.
  6. #4
  7. !~ /m$/
    Devshed Specialist (4000 - 4499 posts)

    Join Date
    May 2004
    Location
    Reno, NV
    Posts
    4,251
    Rep Power
    1810
    I agree that nested tables may not be the best presentation method, but regarding recursion:

    Yeah, my first reaction was that it appears you are combining a subroutine and closure. It's true you could capture the indentation level in a closure, and that may be a clever solution. It isn't necessary though.

    Looking at your data I came up with this:

    Code:
    print traverseLevel($hash);
    
    sub traverseLevel {
    	my ($ref, $i) = @_;
    	$i ||= '';
    
    	foreach my $key (sort keys %$ref) {
    		if ($key =~ /^\d+$/) {
    			printItem($ref->{$key},$i);
    		}
    	}
    }
    
    sub printItem {
    	my ($ref, $indent) = @_;
    
    	return unless ref $ref eq 'HASH';
    	
    	print $indent, "Name: $ref->{'Name'}\n";
    	$indent .= "  ";
    	
    	if (exists $ref->{'children'}) {
    		traverseLevel($ref->{'children'},$indent);
    	}
    }
    As a reminder, that is for this structure:

    Code:
    my $hash = {
    	'3' => {'Name' => 'Parent-3'},
    	'1' => {
    		'children' => {
    			'11' => {'Name' => 'child-1'},
    			'5' => {
    				'children' => {
    					'8' => {'Name' => 'first child of child-2'},
    					'13' => {'Name' => 'second child of child-2'}
    				},
    				'Name' => 'child-2', 
    			}
    		},
    		'Name' => 'Parent-1', 
    	},
    	'2' => {'Name' => 'Parent-2'},
    	'4' => {'Name' => 'Parent-4'},
    };
    It doesn't have to be two separate routines. You can combine them into more if statements: If it's a number do this, if it's 'Name' do this, and if 'children' do this. I just found this to be clearer, though you do have to pass the indentation level around.

    Converting to HTML tables:

    Code:
    #!/usr/bin/env perl
    use strict;
    use warnings;
    
    use CGI qw/:standard/;
    
    my $q = CGI->new;
    
    # as defined earlier
    my $hash = { ... };
    
    print header, start_html, h3('Hierarchy'),"\n\n";
    parentTable($hash);
    print end_html;
    
    sub parentTable {
    	my ($ref, $i) = @_;
    	$i ||= '';
    	
    	print $i, qq{<table id='sort' class='grid' >\n$i<tbody>\n};
    	foreach my $key (sort keys %$ref) {
    		if ($key =~ /^\d+$/) {
    			printHTMLItem($ref->{$key},$i);
    		}
    	}
    	print $i, qq{</tbody>\n$i</table>\n};
    }
    
    sub printHTMLItem {
    	my ($ref, $indent) = @_;
    
    	return unless ref $ref eq 'HASH';
    	
    	$indent .= "\t";
    	
    	if (exists $ref->{'children'}) {
    		print $indent, qq{<tr><td><label>$ref->{'Name'}</label>\n};
    		parentTable($ref->{'children'}, $indent);
    	} else {
    		print $indent, qq{<tr><td>$ref->{'Name'}</td></tr>\n};
    	}
    }

IMN logo majestic logo threadwatch logo seochat tools logo