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

Unpacking to hash, with context

by Fox (Pilgrim)
on Feb 10, 2010 at 12:18 UTC ( [id://822412]=CUFP: print w/replies, xml ) Need Help??

When we got a lot of values to unpack it's a easy task, since perl allows you to concatenate the templates and unpack then in one call.

But not so lucky when some of the values need list context.
Searching through perlmonks I founded this thread which was almost what I needed, except it unpacks everything in scalar context, so I modified it a bit and here we are:
sub unpack2hash { my ($template, $source) = @_; my $hash = {}; foreach(split / /,$template) { my ($temp,$type,$var) = split /:(.)/; if($type eq '@') { my @r = unpack $temp, $source; $hash->{$var} = \@r; my $pack = pack $temp, @r; substr $source, 0, length $pack, ''; }elsif($type eq '$') { my $r = unpack $temp, $source; $hash->{$var} = $r; my $pack = pack $temp, $r; substr $source, 0, length $pack, ''; } else{ die "need context type\n" } } return $hash; }
the template syntax is a bit different, see the example:
my $h = unpack2hash(join(' ',( 'l:$songid', 'c8:@signature', 'l:$genre', 'f:$bpm', 's3:@level', 's:$unk', 'l12:@unk2', 'a24:$genres', 'l2:@unk3', 'a32:$title', 'a32:$subtitle', 'a32:$artist', 'a32:$noter', 'a32:$musicfile', 'l:$jpg', 'l3:@unk4', 'l4:@notepos' )), $data);
each value is separated by whitespace, in the format <template>:<context><key>
this way, the values with @ will be unpacked in list context and the values with $ will be unpacked in scalar context, and stored in the hash with its respective key.

UPDATE: I changed the line $source =~ s/$pack//; to substr $source, 0, length $pack, ''; because $pack was being interpreted as a regex.

Replies are listed 'Best First'.
Re: Unpacking to hash, with context
by Tanktalus (Canon) on Feb 16, 2010 at 19:48 UTC

    ++ for taking unpack and making it nearly readable :-). However, I have to wonder why, if you're writing it anyway, you're joining on spaces, and then splitting on spaces. Wouldn't it be slightly more readable if you did:

    my $h = unpack2hash( [ 'l:$songid', 'c8:@signature', 'l:$genre', 'f:$bpm', 's3:@level', 's:$unk', 'l12:@unk2', 'a24:$genres', 'l2:@unk3', 'a32:$title', 'a32:$subtitle', 'a32:$artist', 'a32:$noter', 'a32:$musicfile', 'l:$jpg', 'l3:@unk4', 'l4:@notepos' ], $data);
    This would arguably be faster (arguable because even going down that road might be a premature optimisation), but I also just think it cleaner. Of course, if you're doing this anyway, you could also just pre-split a bunch of the text, e.g.:
    my $h = unpack2hash( [ [ qw(l $ songid) ], [ qw(c8 @ signature) ], #...
    Also think about reversing the order of the parameters, then we could get rid of the array-ref:
    my $h = unpack2hash( $data, 'l:$songid', 'c8:@signature', #... );
    These are all mere suggestions, so take, or not, as you want - which is why I didn't repeat the previous suggestion to possibly pre-split. Of course, you could also be dynamic and if passed a scalar, split it yourself, but if you're passed an array ref, you could treat it as pre-split. But that would depend on usage.

      However, I have to wonder why, if you're writing it anyway, you're joining on spaces, and then splitting on spaces
      you got me there, in fact was translating php to perl, trying to clone the php's unpack, and when the (lack of) list context hitted me I made these changes, I guess I was so alienated in making the function that I forgot I could change the call, although I ended up changing it anyway..oh well

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://822412]
Approved by Corion
Front-paged by MidLifeXis
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (4)
As of 2024-04-19 03:34 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found