Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Data structure transformation

by ar0n (Priest)
on Apr 07, 2001 at 19:47 UTC ( [id://70722]=perlquestion: print w/replies, xml ) Need Help??

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

I'm working on a student registration application for my school, which uses HTML::Template.

It grabs data from a database and needs to transform that data into a data structure that HTML::Template can use.

Each row looks like this:
while ( $sth->fetchrow() ) { push @classes, { class => $class, department => $department, count => $count }; }
The structure my template needs looks like this:
@departments = ( { department => "v6" classes => [ { class => "Chemistry", department => "v6", count => 18 }, { class => "German I", department => "v6", count => 27 }, etc ... ] }, { department => "h4" classes => [ { class => "French II", department => "h4", count => 9 }, etc ... ] }, etc ... );
Now, to get to the point; I'm using the following to get what I want:
my %departments; push @{ $departments{ $_->{department} }{classes} }, $_ foreach (@clas +ses); my @departments = map { $_->{department} = $_->{classes}[0]{department}; $_ } values %departments;
And it works, but.... it just rubs me the wrong way (take that Christina Aguilera!). It's icky. I don't like that temporary hash very much.

Is there a more efficient/prettier way of getting what I want? I'm not really aiming for high-performance, since the query I'm using will never return over a 1000 records, but I'm curious...

Thanks.

[ar0n]

Replies are listed 'Best First'.
Re: Data structure transformation
by danger (Priest) on Apr 07, 2001 at 20:55 UTC

    I'd be more inclined to remove the @classes array (unless it makes other things in your code more convenient) -- and push onto a departments hash to begin with. Something like:

    my %departments; while( $sth->fetchrow() ) { push @{$departments{$department}}, { class => $class, department => $department, count => $count, }; } my @departments = map { { department => $_, classes => $departments{$_} } } keys %departments;

    You could also sort on the keys to create the final array sorted by department if desired.

      This is pretty much what I've been looking for...

      Something seemed redundant, and I assumed it to be %departments, but you're right; @classes is not really needed.

      And ++ on the sort. I forgot to sort my keys, since it showed up fine when I was testing, but that was due too a not large enough dataset :-)

      Thanks.

      Not that it means anything of course, since any improvement I get here means a speed increase of thousandths of a second (guessing), but I was curious....

      [ar0n]

Re: Data structure transformation
by araqnid (Beadle) on Apr 07, 2001 at 20:29 UTC
    I can't eliminate the need for a temporary hash: having attempted it, I'm inclined to think it's not a nice thing to do, since you always seem to end up needing to collate as a side-effect of iterating over the input... Anyway, here's what I got:
    #!/usr/bin/perl -w require 5.6.0; use strict; use Data::Dumper; our @input = ({ class => "Chemistry", department => "v6", count => 18 +}, { class => "German I", department => "v6", count => 27}, { class => "French II", department => "h4", count => 9 } +); our %collate; foreach (map { [ $_->{department}, $_ ] } @input) { push(@{$collate{$$_[0]}}, $$_[1]); } our @output = map { { department => $_, classes => $collate{$_} } } ke +ys %colla\ te; print Data::Dumper->new([\@output], [qw|*output|])->Dumpxs;
    (Subquestion: is that constuct I used in the foreach() what's called a Schwartian transform?)

    Update:

    (Subanswer: It isn't)

    Clearly I'm insufficiently awake. %collate can be built like this:

    foreach (@input) { push(@{$collate{$_->{department}}}, $_); }
    The transform is redundant.

    Update II

    tilly suggested I point out that the code doesn't work on older Perls, even 5.005.

    Afaict, the changes for perl 5.005 would be:

    • replace "require 5.6.0" to the appropriate version
    • either change "our $foo" into "my $foo", or separate the declaration and assignments, and declare them in a "use vars qw($foo)" construct.
(dkubb) Re: (2) Data structure transformation
by dkubb (Deacon) on Apr 09, 2001 at 03:25 UTC

    ar0n, I think you are on the right track. I also try to limit the usage of sythentic variables, however IMHO using hashes isn't as wasteful as you might think. Their ability for fast lookups and their natural grouping properties make them a perfect choice for this problem.

    The key is to use the hash to group your data, then flatten it into an array when you are done. Please consider the following code:

    my %departments; while($sth->fetch) { $departments{ $department }{department} ||= $department; push @{ $departments{ $department }{classes} }, { class => $class, department => $department, count => $count, }; }

    Now you need to transform it into a structure that HTML::Template will understant and use. The entire first level of the hash, the keys, are redundant, and something we don't need now. So we'll slice it away and flatten the structure into and array reference:

    $tmpl->param( departments => [values %departments], ); #Note: $tmpl is an HTML::Template object

    Or if you'd like to sort the department output:

    $tmpl->param( departments => [@departments{ sort keys %departments }], );

    Update 1: Removed extra code that was not pertinent to the problem. Also, changed the while loop code to be closer in-line to what ar0n had.

    Update 2: Added sort to flattening example.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://70722]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (3)
As of 2024-04-18 04:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found