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

Current data:

237 : AMAFILE: AMA210 238 : STATUS: NETWORK 239 : DATE CREATED: 9/20/01 240 : 241 : AMAFILE: AMA211 242 : STATUS: NETWORK 243 : DATE CREATED: 9/20/01 244 : 245 : AMAFILE: AMA212 246 : STATUS: NETWORK 247 : DATE CREATED: 9/20/01 248 : 249 : AMAFILE: AMA213 250 : STATUS: CLOSED 251 : DATE CREATED: 9/20/01 252 : 253 : AMAFILE: AMA214 254 : STATUS: OPEN 255 : DATE CREATED: 9/20/01

Desired:

I would like each block (here seperated by the empty space after each colon) to be a hash within an array. <readmore>

Therefore:

$myarray[253] = ( $myhash{AMAFILE} = "AMA214"; $myhash{STATUS} = "OPEN"; $myhash{DATE CREATED} = "9/20/01");

My difficulty here is that I am doing this whole thing in a map{} where each element of $_ is each line (the counter above indicates each $_). Now, I have my ideas of how to do this. That is not the problem. I am curious to see what others may think the *best* way to do this is.
One way I could do this is a brute force attack using my counter where I could push() a temporary array for 3 iterations of the map{} loop so that I could get my data for my one iteration of my array of hashes. Then split that temporary array up into my hashes and assign those hashes to my main array of hashes. I hope that made sense. This is the direction Im leaning toward going but if anything is apparent in Perl there is always a cleaner, easier way of doing things. So, before I "waste" my time on a down and dirty implication of this I thought I would post this to the rest of the monk kingdom.

Here is my code so far:

#!/usr/local/bin/perl -w use strict; use File::Basename; use Env; my (%SITREP, $param, $value, @SORTME); my $script = "$HOME/DSC/bin/checkDisplayBatch"; my $base = basename($script); my $c = 0; eval { $SIG{ALRM} = sub { print "Timeout during operation!\n"; exit(5); }; }; open(CDB,"$script |"); # Set a timeout value so that it doesn't hang! alarm(30); while ( <CDB> ) { chomp; die("$base failed probably due to the X25 link being saturated rig +ht now.\n". "Try again in a moment...". "Dump from $script: $!\n\n") if ( /ERROR/ ); next if ( /-/ ); if ( /AMA/ or /STATUS/ ) { push(@SORTME,split(/\|/)); } } # So far this is the beginning of my implementation # of what I want to do. This is the code (partly # tweaked now) that gave the above output of data # that I am working with. This, in other words, is # where my work, I think, needs to be worked on more. # There is some code in here that I have just placed # to test and I know it will not totally work. Just # disregard that, please ;) map { $_ =~ s/\s//; # print $c++ ." : $_\n"; <-- obviously this is the code # that gave us the above output if ( /\w/ ) { ($param,$value) = split(/:/); $value =~ s/\s+//g; $array[$c] = ($SITREP{$param} = $value); if ( $value eq "CLOSED" ) { print ""; } } else { $c++; } } @SORTME;

----------
- Jim

Replies are listed 'Best First'.
Re: Suggestions on how to make array of hashes with this...
by merlyn (Sage) on Sep 21, 2001 at 02:42 UTC
    if the data is always in that order, I'd just go for broke:
    $_ = join "", @SORTME, "\n\n"; while (/(AMAFILE):\s+(.*)\n(STATUS):\s+(.*)\n(DATE CREATED):\s+(.*)/g) + { push @array, { $1, $2, $3, $4, $5, $6 }; }
    If you can't count on the order (although your example was entirely ordered), then it's a bit harder, but still pretty straightforward.

    -- Randal L. Schwartz, Perl hacker

      Yes sir. The order will never change...er well, it shouldn't. I wrote the script that this pulls from though so I should be able to doc it pretty well.

      So, for your example then, let me make sure I got it straight what you are doing.

      You are joining each element in $_ to @SORTME (don't quite follow the purpose of the two carriage returns after your join though. Then you are taking $_ in your while loop and extracting AMAFILE, STATUS, and DATE CREATED and their values into $1..$6 and pushing them into the array @array, right? I noticed something different about this array though. Is this a multi-dim array?

      ----------
      - Jim

Re: Suggestions on how to make array of hashes with this...
by blakem (Monsignor) on Sep 21, 2001 at 02:51 UTC
    How about creating an array of hashes (@records) like this:
    #!/usr/bin/perl -wT use strict; my (@records,%tmprecord); while (<DATA>) { my ($id,$field,$data) = (split(/[\s:]+/)); if ($field) { $tmprecord{$field} = $data; } else { push(@records,{%tmprecord}); # OR PERHAPS '$records[$id] = {%tmpre +cord};' undef %tmprecord; } } for my $record (@records) { print "$_ => $record->{$_}\n" for (keys %$record); print "\n"; } __DATA__ 237 : AMAFILE: AMA210 238 : STATUS: NETWORK 239 : DATE CREATED: 9/20/01 240 : 241 : AMAFILE: AMA211 242 : STATUS: NETWORK 243 : DATE CREATED: 9/20/01 244 : 245 : AMAFILE: AMA212 246 : STATUS: NETWORK 247 : DATE CREATED: 9/20/01 248 : 249 : AMAFILE: AMA213 250 : STATUS: CLOSED 251 : DATE CREATED: 9/20/01 252 : 253 : AMAFILE: AMA214 254 : STATUS: OPEN 255 : DATE CREATED: 9/20/01

    -Blake

      This is actually what I was trying to do (I thought). It's late though so I will read through this code tomorrow more thoroughly.

      ----------
      - Jim

Re: Suggestions on how to make array of hashes with this...
by Zaxo (Archbishop) on Sep 21, 2001 at 03:14 UTC

    Do the numbers in the first column matter? They appear to be line numbers in the data file. Using them as array indexes will leave only the 4n+1 ones defined.

    Assuming they don't matter, I notice that they are grouped so that the number divided by 4 is the same within a group. Therefore:

    my @aryhash; while (<HANDLE>) { chomp; my @dat = split " ", $_; # magical whitespace split $aryhash[$dat[0]/4|0]->{$dat[2]} = $dat[3]; }
    This is a pretty direct way to do it, but is tightly bound to your data format.

    After Compline,
    Zaxo

      The numbers in the first column are not actually a part of the dataset. I used those numbers strictly for example only but they can most definitely be used for array indices. This reply most intrigues me since the dataset should always be the same and you are using a mathematical means to resolve the issue. I might try this one out for fun to see how it works. Using math to solve problems like this has always intrigued me.

      ----------
      - Jim

Re: Suggestions on how to make array of hashes with this...
by snafu (Chaplain) on Sep 22, 2001 at 00:08 UTC
    Ok, this is the direction I chose to go. Any thoughts, ideas, constructive criticism, please be gentle. :)

    Thanks guys.

    ... while ( <CDB> ) { chomp; die("$base failed probably due to the X25 link being saturated rig +ht now.\n". "Try again in a moment...". "Dump from $script: $!\n\n") if ( /ERROR/ ); next if ( /-/ ); if ( /AMA/ or /STATUS/ ) { $_ =~ s/\|//; if ( /\w/ ) { if ( $c == 3 ) { push(@ARRAY,{%SITREP}); $c = 0; } else { for ( split(/\|/) ) { $_ =~ s/\s//; ($param,$value) = split(/:/); $value =~ s/\s+//g; $SITREP{$param} = $value; $c++; } } } } } print "There are ".scalar @ARRAY." values in \@ARRAY\n"; #print "$ARRAY[0]{AMAFILE}\n"; <- example for my $element ( 0 .. $#ARRAY ) { for my $key ( keys(%{ $ARRAY[$element] }) ) { print "$element $role is $ARRAY[$i]{$key}\n"; } } sub debug { my $msg = "@_"; print STDERR "$msg" if DEBUG; }

    ----------
    - Jim