Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Splitting a hashref into hashrefs

by legLess (Hermit)
on Mar 20, 2003 at 03:44 UTC ( [id://244526]=perlquestion: print w/replies, xml ) Need Help??

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

Monks ~

I'm struggling with the very laudable goal of making my HTML templates as easy to use as possible, and hoping I can learn some new Perlery while I'm at it. I'm using Slash and Template Toolkit, but I think the Slash methods are clear enough so no knowledge of it is needed to understand the code (yeah, that surprised me too once I started hacking it :). My solution below works, but strikes me as ugly.

The Problem

I'm getting a hashref (cunningly named '$hashref' for your reading pleasure) back from a database:
my $hashref = $slashdb->sqlSelectHashref( # SELECT 'o.org_id, o.name, g.group_id, g.name', # FROM 'groups AS g, orgs AS o', # WHERE "group_id = $group_id AND o.org_id = g.org_id", );
...with the hope of passing it roughly like this (where '$org' and '$group" are hashrefs):
slashDisplay( 'group_edit', { org => $org, group => $group, });
...into this template:
<p>Org id: [% org.id %]</p> <p>Org name: [% org.name %]</p> <p>Group id: [% group.id %]</p> <p>Group name: [% group.name %]</p>

First attempt: splitting hashrefs in the database return

I tried a few ways of doing this in the database call, but I confess that my Perl wasn't up the the task. I was hoping to pass the data to two hashrefs (e.g. $orgs and $groups) but I couldn't figure out how to slice up the returned hashref into two new hashrefs. Doing it in the database call would also allow me write the SELECT part of the query as written above rather than what follows, which is what I've used to prevent key collisions in the hashref:
'o.org_id, o.name AS org_name, g.group_id, g.name AS group_name',

An arrayref won't work

I could get an arrayref from the database -- these are easy to split -- but that would mean using (e.g.) [% org.1 %] in the template, which isn't very readable and breaks when the field order changes.

One hashref won't work

I could also pass the hashref directly to the template:
slashDisplay( 'group_edit', { hashref => $hashref });
...which is simple in the code but would mean this in the template:
<p>Org id: [% hashref.org_id %]</p> <p>Org name: [% hashref.org_name %]</p> <p>Group id: [% hashref.group_id %]</p> <p>Group name: [% hashref.group_name %]</p>

What worked

In the end I came up with this: constructing an anonymous hashref and passing it to the template. It gives me total control of the named parameters I pass to the template and ensures that the HTML guy (also me :) has the easiest experience possible.
slashDisplay( 'group_edit', { org => { id => $hashref->{ org_id }, name => $hashref->{ org_name }}, group => { id => $hashref->{ group_id }, name => $hashref->{ group_name }}, });
The only thing I don't like is that while the template has been cleaned up the code is much gnarlier. On balance I think this is a good trade-off, but I wonder if my ignorance is setting up a false dichotomy. Is there a less gnarly way of doing this (or at least a way with less typing)?
--
Man With No Legs, Inc.

Replies are listed 'Best First'.
Re: Splitting a hashref into hashrefs
by submersible_toaster (Chaplain) on Mar 20, 2003 at 04:01 UTC

    Well, gnarly is a pretty subjective term .. you might save some typing by...

    my (%org, %group); @org{qw/id name/} = @hashref{qw/org_id org_name/}; @group{qw/id name/} = @hashref{qw/group_id group_name/}; slashDisplay( 'group_edit', { org => \%org , group => \%group });

    You might extend that further and replace those {qw/in name/} parts with a predefined array like...

    @slash_org_fields=qw/id name/; @db_org_fields=qw/org_id org_name/; # Then just use them in place @org{@slash_org_fields} = @hashref{@db_org_fields};
    Which may or may not present you with greater gnarlyness. I can see the advantages of filling those two temporary hashes together, rather than in the call to slashDisplay()

    If I missed the point I apologise - there's a war on you know


    I can't believe it's not psellchecked
Re: Splitting a hashref into hashrefs
by Aragorn (Curate) on Mar 20, 2003 at 09:35 UTC
    In my opinion, the name $hashref is not a very descriptive name. Looking at the code, I'd say that $group_info communicates more clearly the meaning of the data. You retrieve the "org" and "group" data in a single SQL statement, so like perrin says in Re: Splitting a hashref into hashrefs, the can be assumed to be related. If not, you should rethink the query, and maybe split them in two parts, after which the template can be as you want it.

    If they are related, a renaming of variables can be very helpful:

    my $group_info = $slashdb->sqlSelectHashref( # SELECT 'o.org_id, o.name, g.group_id, g.name', # FROM 'groups AS g, orgs AS o', # WHERE "group_id = $group_id AND o.org_id = g.org_id", );
    Now, the call to slashDisplay() will be:
    slashDisplay('group_edit', { group_info => $group_info });
    And the template will look like:
    <p>Org id: [% group_info.org_id %]</p> <p>Org name: [% group_info.org_name %]</p> <p>Group id: [% group_info.group_id %]</p> <p>Group name: [% group_info.group_name %]</p>
    This way, the code remains simple, and the template is perfectly understandable.

    Arjen

Re: Splitting a hashref into hashrefs
by perrin (Chancellor) on Mar 20, 2003 at 05:07 UTC
    One hashref won't work? Why the heck not? Clearly these things represent a single record, since you selected them with a join. Separate hashrefs would mean these things are not related.
      One of my explicit goals is to make the HTML templates as easy to use as possible. I hold that this template:
      <p>Org id: [% org.id %]</p> <p>Org name: [% org.name %]</p> <p>Group id: [% group.id %]</p> <p>Group name: [% group.name %]</p>
      ...is more intuitive and easier to use than this one, which is what I need if I use a single hashref:
      <p>Org id: [% hashref.org_id %]</p> <p>Org name: [% hashref.org_name %]</p> <p>Group id: [% hashref.group_id %]</p> <p>Group name: [% hashref.group_name %]</p>
      My other goal is to learn a few other ways of doing this, perhaps even a cool way to split a hashref into two hashrefs.

      Most of this is copied from my post; perhaps you should have read it more carefully?
      --
      man with no legs, inc.
        I read it and understood; I just don't agree with your ideas about data structures for templating. I believe you should only nest your data structures when the data actually requires it, which is not the case here. If I were doing this, I would get rid of that top hashref and set it up like this:
        <p>Org id: [% org_id %]</p> <p>Org name: [% org_name %]</p> <p>Group id: [% group_id %]</p> <p>Group name: [% group_name %]</p>
        I would only use another level below this if there are multiple values. For example, if there are multiple groups per organization, I would represent groups as an array of hashes, so I could make a template like this:
        <p>Org id: [% org_id %]</p> <p>Org name: [% org_name %]</p> [% FOREACH group = groups %] <p>Group id: [% group.id %]</p> <p>Group name: [% group.name %]</p> [% END %]

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (3)
As of 2024-04-26 02:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found