Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Accessing an array of anonymous hashes

by thezip (Vicar)
on Mar 26, 2007 at 16:58 UTC ( [id://606617]=perlquestion: print w/replies, xml ) Need Help??

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

I'm working on a problem where I have to produce a fixed-length string (record) that is comprised of N columns, each of varying width.

ie.:

my $str = "AAABBBBBBBBC";

where:

  "AAA" represents the value for "Batch_Agency"
  "BBBBBBBB" represents the value for "Batch_Date"
  "C" represents the value for "Batch_Type"

I'm concerned with these two data:

  • The field name
  • The columnar positions that this field occupies. (In the final output, columns will be padded to ensure that they have the correct width)

Once I get the data structure right, I'll use pack to produce the format specification and the fixed-length record.

I began setting up my data structure by creating the array of anonymous hashes as shown below in method 1:

#! perl -w use strict; use Data::Dumper; my $transaction_record_fields_method1 = [ { 'Batch_Agency' => [ 1 .. 3] }, { 'Batch_Date' => [ 4 .. 11] }, { 'Batch_Type' => [ 12 .. 12] }, ];

This was not very useful, since I wasn't able to come up with a way access the field name and column position array using this method.

If I change the data structure, I can successfully access the required elements by using either method2 or method3:

# Accessing an AoA my $transaction_record_fields_method2 = [ [ 'Batch_Agency', [ 1 .. 3] ], [ 'Batch_Date', [ 4 .. 11] ], [ 'Batch_Type', [ 12 .. 12] ], ]; print Dumper($transaction_record_fields_method2->[2][0]); print Dumper($transaction_record_fields_method2->[2][1]); # Accessing a plain ol' AoH my $transaction_record_fields_method3 = [ { name => 'Batch_Agency', range => [ 1 .. 3], }, { name => 'Batch_Date', range => [ 4 .. 11], }, { name => 'Batch_Type', range => [ 12 .. 12], }, ]; print $transaction_record_fields_method3->[2]{name}, "\n"; print Dumper($transaction_record_fields_method3->[2]{range});

Since my knee-jerk reaction was to set up the data structure using method1, I'd like, if possible, for someone to explain to me how to access the fieldname and column position array ref for each field using method1.

Thanks!

Updated: Data structure in method2


Replies are listed 'Best First'.
Re: Accessing an array of anonymous hashes
by mreece (Friar) on Mar 26, 2007 at 17:07 UTC
    i think you'll have to use the keys function:
    print Dumper( keys %{ $transaction_record_fields_method1->[2] } ); print Dumper( values %{ $transaction_record_fields_method1->[2] } );
    keep in mind keys will return a list, so:
    my ($name) = keys %{ $transaction_record_fields_method1->[2] }; my $range = $transaction_record_fields_method1->[2]->{$name};

      Yes, thanks. I did try this initially, and it was the only way that I could get it to work. I slowly began to realize that I was probably using the wrong data structure when I had to do this...

      Alternatively, is there a means, through clever indexing or such, that will also get me to the values I seek?

        you can dereference the hash, then treat it as a list:
        print Dumper( (%{ $transaction_record_fields_method1->[2] })[0] ); print Dumper( (%{ $transaction_record_fields_method1->[2] })[1] );
        but it probably makes more sense to just use an AoA.
Re: Accessing an array of anonymous hashes
by ferreira (Chaplain) on Mar 26, 2007 at 18:16 UTC

    For the particular problem you're approaching, it looks like the structure of a list of pairs is quite adequate.

    ( Batch_Agency => { range => [ 1 .. 3] }, Batch_Date => { range => [ 4 .. 11] }, Batch_Type => { range => [ 12 .. 12 ] } }
    With the list of pairs, you get the features:
    • every field has a name
    • the order is preserved
    So it should be quite easy with a little processing both to format the records and to map the input given as array of hash refs to the right positions and shapes.

    Beware of using the ranges as you mentioned (range => [1 .. 3]). They can become a burden to maintain as fields are added or change size. It would probably be more useful to work only with lengths and compute automatically the range (if needed). Something like length => 3 or format => 'A3'.

    If you decide to avoid the suffering of writing and maintaining the code for producing these streams of fixed-format records, you may try some modules from CPAN. Among them, Data::FixedFormat and AnyData::Format::Fixed.

      Thanks for your reply.

      As a clarification, I was told that the specification I was provided would probably never change (or would at least require an act of the Oregon State Government to change it). There are perhaps hundreds of machines that use this particular format, so I feel reasonably safe hard-coding a few things.

      My goal is to localize all of the hard-coded items in one place to minimize the risk you mention.

      As for the column ranges, this is the way they are portrayed in the specification (ie. as a range of numbers like [59 .. 63]. I plan on calculating the column widths based upon the values that I am supplied, and then constructing the pack specification from that.

Re: Accessing an array of anonymous hashes
by Not_a_Number (Prior) on Mar 26, 2007 at 18:54 UTC

    I may be way out of line here, but I think you might have an XY Problem. Let me state your problem as I understand it, and please correct me (or just ignore this post) if I'm wrong.

    You have three variables (we don't care where they came from). Let's call them:

    my $agency = 'XY'; # A string between 1 and 3 characters long my $date = '2007/6/3'; # Or whatever date format my $type = 'T'; # A single character

    and you want to concatenate their values together into a 12-character string, padding them out if necessary (I assume with spaces in the text field, and with zeroes in the date field) so that they will be respectively 3, 8 and 1 characters long. In other words, the required output for the example data above would be:

    XY 20070603T

    If so - and I apologise again if I've missed something - why not simply use a combination of join and sprintf?

    my $string = join '', sprintf( '%-3s', $agency ), sprintf( '%04d%02d%02d', split /\//, $date ), $type;

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (3)
As of 2024-03-29 15:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found