Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks, Ok, so I have a table of mapped IDs. It contains 3 fields: project_id, variable_id and symbol_id. The relationships are: project_id 1..* variable_id variable_id 1..1 symbol_id The following is a simple example:
p_id | v_id | s_id 1 | 1 | 1 2 | 1 | 2 3 | 1 | 3 4 | 1 | 4 1 | 2 | 5 2 | 2 | 6 3 | 2 | 7 4 | 2 | 8 2 | 3 | 9 4 | 3 | 10 1 | 4 | 11 2 | 4 | 12 4 | 4 | 13 3 | 5 | 14
this would be stored in an AoA called @results, where each row is an array of project_id, variable_id and symbol_id, where a row is an element of @results. From this I would like to create a table like so:
| p_1 | p_2 | p_3 | p_4 | --------------------------------- v_1 | s_1 | s_2 | s_3 | s_4 | v_2 | s_5 | s_6 | s_7 | s_8 | v_3 | | s_9 | | s_10 | v_4 | s_11 | s_12 | | s_13 | v_5 | | | s_14 | |
It doesnt matter too much if the pipelines are there or not, I just need the data aligned for easy reading. At the moment I use nested for loops to generate this. This becomes a problem when a symbol doesnt exist for a particular variable in a project. The way it works is it appends a symbol to a string named $row when it exists...if it doesnt nothing happens, so I get something like the following table...
| p_1 | p_2 | p_3 | p_4 | --------------------------------- v_1 | s_1 | s_2 | s_3 | s_4 | v_2 | s_5 | s_6 | s_7 | s_8 | v_3 | s_9 | s_10 | v_4 | s_11 | s_12 | s_13 | v_5 | s_14 |
Any ideas how I might solve this? Thanks, Steve

Replies are listed 'Best First'.
Re: Create a table from a matrix
by TedPride (Priest) on May 24, 2006 at 17:57 UTC
    Perhaps a bit more messy than necessary, but:
    use strict; use warnings; my ($c, %k, %vals, %cols, @cols, @table, $row); chomp($_ = <DATA>); s/\s+//g; $c = 0; $k{$_} = $c++ for split /\|/, $_; while (<DATA>) { s/\s+//g; @_ = split /\|/, $_; $vals{@_[$k{'v_id'}]}{@_[$k{'p_id'}]} = @_[$k{'s_id'}]; $cols{@_[$k{'p_id'}]} = (); } @cols = sort { $a <=> $b } keys %cols; push @table, ['', map { 'p_'.$_ } @cols]; for $row (sort { $a <=> $b } keys %vals) { push @table, ['v_'.$row, map { $vals{$row}{$_} ? 's_'.$vals{$row}{ +$_} : '' } @cols]; } table(\@table); sub table { my ($arr, $i, @lengths, $length, $format) = $_[0]; for (@$arr) { for $i (0..$#$_) { $lengths[$i] = length($_->[$i]) if !$lengths[$i] || $lengt +hs[$i] < length($_->[$i]); } } $length = 0; $length += $_ for @lengths; $length += 3 * $#lengths + 2; $format = join ' | ', map { '%-'.$_.'s' } @lengths; for ($i = 0; $i < $#$arr; $i++) { print ' ', sprintf($format, @{$arr->[$i]}), "\n", '-' x $length, "\n"; } print ' ', sprintf($format, @{$arr->[-1]}), "\n"; } __DATA__ p_id | v_id | s_id 1 | 1 | 1 2 | 1 | 2 3 | 1 | 3 4 | 1 | 4 1 | 2 | 5 2 | 2 | 6 3 | 2 | 7 4 | 2 | 8 2 | 3 | 9 4 | 3 | 10 1 | 4 | 11 2 | 4 | 12 4 | 4 | 13 3 | 5 | 14
Re: Create a table from a matrix
by saintmike (Vicar) on May 24, 2006 at 17:04 UTC
Re: Create a table from a matrix
by davidrw (Prior) on May 24, 2006 at 17:49 UTC
    can you post your code so we can see/adjust what you're doing?

    from scratch, here's a (untested) solution. Basically, instead of the concat building of a $row string, you map to loop over the cols, and also use join. But first hash everything up.
    my %all = ( p => {}, v => {} ); my %s; foreach my $row ( @results ){ $all{p}->{ $row->{p_id} } = 1; $all{v}->{ $row->{v_id} } = 1; $s{ $row->{v_id} }->{ $row->{p_id} } = $row->{s_id}; } my @p = sort keys %{$all{p}}; my @v = sort keys %{$all{v}}; print join("|", " ", map { sprintf(" p_%-2d ",$_) } @p ), "\n"; print "-"x(7*(@p+1)) . "\n"; foreach my $v ( @v ){ # note hash slice print join( "|", # delim sprintf(" v_%-2d ",$v), # first column map { defined($_) ? sprintf(" s_%-2d ",$_) : " " # format acc +ordingly } @{$s{$v}}{ @p } # all the s values for the 'p' colum +ns; done via a hash slice ), "\n"; }
Re: Create a table from a matrix
by ruzam (Curate) on May 24, 2006 at 22:31 UTC
    Another (tested) solution
    use strict; use warnings; my @results = ( [1, 1 , 1], [2, 1 , 2], [3, 1 , 3], [4, 1 , 4], [1, 2 , 5], [2, 2 , 6], [3, 2 , 7], [4, 2 , 8], [2, 3 , 9], [4, 3 , 10], [1, 4 , 11], [2, 4 , 12], [4, 4 , 13], [3, 5 , 14] ); my %table; my %cols; foreach my $row (@results) { $table{$row->[1]}->{$row->[0]} = $row->[2]; # track the columns separate $cols{$row->[0]}++; } my @cols = sort { $a <=> $b } keys %cols; # print table headings print " |"; foreach (@cols) { printf(" p_%-2d |",$_) } print "\n" . ("-" x (7 * (@cols + 1))) . "\n"; # now print rows foreach my $row (sort { $a <=> $b } keys %table) { printf(" v_%-2d |", $row); foreach my $col (@cols) { if (defined $table{$row}->{$col}) { printf(" s_%-2d |", $table{$row}->{$col}); } else { print " |" } } print "\n"; }
Re: Create a table from a matrix
by Praveen (Friar) on May 26, 2006 at 11:15 UTC
    Try This
    while(<DATA>) { my ($pid,$vid,$sid) = split(/\|/,$_); $pid =~ s/\s*//g; $vid =~ s/\s*//g; $sid =~ s/\s*//g; $hs{$vid}{$pid}="s_".$sid, if($vid =~ /\d/); } print" | p_1 | p_2 | p_3 | p_4 \n"; print "----------------------------------\n"; for $fst (1..5) { print "v_$fst"; printf(" | %4s", $hs{$fst}{$_}), foreach (1..4); print "\n"; print "----------------------------------\n"; } __DATA__ p_id | v_id | s_id 1 | 1 | 1 2 | 1 | 2 3 | 1 | 3 4 | 1 | 4 1 | 2 | 5 2 | 2 | 6 3 | 2 | 7 4 | 2 | 8 2 | 3 | 9 4 | 3 | 10 1 | 4 | 11 2 | 4 | 12 4 | 4 | 13 3 | 5 | 14
    Rds/Praveen
Re: Create a table from a matrix
by Anonymous Monk on May 25, 2006 at 12:23 UTC
    Thanks for your responses, they have helped a lot...but could you possibly provide some comments with your code so I can understand it better...im pretty inexperienced with Perl. Thanks.