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

I'm ducking a %hash or a db, even though the question plainly implies use of one or the other, for what may be (in this particular case) a semi-good reason, and I'm curious about better ways to handle to particular data.

Given the two arrays (below), each comprised of multiple sets of elements, and which share, db-like, a first element in each set*1, is this code demonstrating yet another of my blind spots or an unnecessarily clumsy way to output related sets?

*1 The the comments after each array assignment may explain this more clearly.
#!/usr/bin/perl use strict; use warnings; my @arr = ("1", "Smith", "Ric", "1", "Smith", "Rich", "1", "abc", "A", + "1", "cdeQ", "C Sr", "1", "ghi", "G", "2", "cdeQ", "C Sr", "3", "ghi +", "G", "3", "abc", "A", "3", "Smith", "Ric", "4", "cde", "C", "5", " +cde", "C", "5", "xyz123", "X", "5", "cdeQ", "C II", "5", "Smith", "Ri +ch"); # callno lname fi (repeat 3 data points for each FF at each + callno) my @arr2 = ("1", "S", "01/05", "04", "2", "EMS", "01/05", "07", "3", " +VF", "01/05", "11", "4", "S", "01/05", "13", "5", "B", "01/05", "17") +; # callno type date hr (repeat 4 data points for each in +cident) my ($i, $j, $seen); $i = 0; $j = 0; for my $arr2(@arr2) { if ( $arr2[$j+3] ) { # ie, if there's a complete call r +ecord print "$arr2[$j] " . "$arr2[$j+1] " . "$arr2[$j+2]" . "$arr2[$ +j+3]\n"; $seen = substr($arr2[$j], 0); print "\tDEBUG: My \$seen: $seen\n\n"; $j += 4; } else { print "\n\t\t No more calls\n"; exit; } for my $arr(@arr) { if ( ($arr[$i]) && ($seen == substr($arr[$i], 0)) ) { print "$arr[$i] " . "$arr[$i+1] " . "$arr[$i+2] " . "\n"; $i += 3; next; } else { print "\n\t\tFF Array exhausted for Callno $seen\n"; last; } } next; }

This "works" in that the output is in a form the users can live with:

1 S 01/0504 DEBUG: My $seen: 1 1 Smith Ric 1 Smith Rich 1 abc A 1 cdeQ C Sr 1 ghi G FF Array exhausted for Callno 1 2 EMS 01/0507 DEBUG: My $seen: 2 2 cdeQ C Sr FF Array exhausted for Callno 2 3 VF 01/0511 DEBUG: My $seen: 3 3 ghi G 3 abc A 3 Smith Ric FF Array exhausted for Callno 3 4 S 01/0513 DEBUG: My $seen: 4 4 cde C FF Array exhausted for Callno 4 5 B 01/0517 DEBUG: My $seen: 5 5 cde C 5 xyz123 X 5 cdeQ C II 5 Smith Rich FF Array exhausted for Callno 5 No more calls

Suggestions of YAM (™ "yet another method") would also be welcome.

Replies are listed 'Best First'.
Re: Iterating two co-dependant arrays
by ikegami (Patriarch) on Jan 07, 2010 at 18:39 UTC
    • substr($arr2[$j], 0) can be written as $arr2[$j]
      substr($arr[$i], 0) can be written as $arr[$i]
    • next is useless in for (...) { ...; next; }
    • exit should be last.
    • Those are awful var names.
    my @ff_fields = qw( callno lname fname ); my @incident_fields = qw( callno type date hr ); while (@ffs) { my %ff; @ff{@ff_fields} = splice(@ffs, 0, 0+@ff_fields); print("@ff{@ff_fields}\n"); while (@incidents && $incidents[0] == $ff{callno}) { my %incident; @incident{@incident_fields} = splice(@incidents, 0, 0+@incident_fields); print(" @incident{@incident_fields}\n"); } }
    or
    my @ff_fields = qw( callno lname fname ); my @incident_fields = qw( callno type date hr ); my %incidents_by_callno; while (@incidents) { my %incident; @incident{@incident_fields} = splice(@incidents, 0, 0+@incident_fields); push @{ $incidents_by_callno{ $incident{callno} } }, \%incident; } while (@ffs) { my %ff; @ff{@ff_fields} = splice(@ffs, 0, 0+@ff_fields); print("@ff{@ff_fields}\n"); for my $incident (@{ $incidents_by_callno{ $ff{callno} } }) { print(" @$incident{@incident_fields}\n"); } }

    I used a hash for the record because it'll help when you expand the code.

Re: Iterating two co-dependant arrays
by jwkrahn (Abbot) on Jan 07, 2010 at 18:28 UTC

    It looks like you need to either use a hash, or if the keys repeat then use an array of arrays.



    for my $arr2(@arr2) {

    You are not using the $arr2 variable anywhere so why are you declaring it?    You are iterating over the elements of the array but are using indexing instead so your for loop should look like this instead:

    for ( my $j = 0; $j <= $#arr2; $j += 4 ) {


    $seen = substr($arr2[$j], 0);

    That is the same as saying:

    $seen = $arr2[$j];
      It looks like you need to either use a hash, or if the keys repeat then use an array of arrays.

      A hash would be better in both cases; if keys repeat then use a hash of arrays. Or a database. Sqlite has excellent performance and is very easy to set up.

Re: Iterating two co-dependant arrays
by thundergnat (Deacon) on Jan 07, 2010 at 18:52 UTC

    It's hard to see why you would want to avoid a hash or database since this seems to be exactly what you need.

    That being said, here is a version using arrays of arrays. It's quite inefficient as it cycles through the @arr array for every line of @arr2, but it doesn't use a hash or database.

    #!/usr/bin/perl use strict; use warnings; my @arr = ( [ "1", "Smith", "Ric" ], [ "1", "Smith", "Rich" ], [ "1", "abc", "A" ], [ "1", "cdeQ", "C Sr" ], [ "1", "ghi", "G" ], [ "2", "cdeQ", "C Sr" ], [ "3", "ghi", "G" ], [ "3", "abc", "A" ], [ "3", "Smith", "Ric" ], [ "4", "cde", "C" ], [ "5", "cde", "C" ], [ "5", "xyz123", "X" ], [ "5", "cdeQ", "C II" ], [ "5", "Smith", "Rich" ] ); # callno lname fi (repeat 3 data points for each FF at each callno) my @arr2 = ( [ "1", "S", "01/05", "04" ], [ "2", "EMS", "01/05", "07" ], [ "3", "VF", "01/05", "11" ], [ "4", "S", "01/05", "13" ], [ "5", "B", "01/05", "17" ] ); # callno type date hr (repeat 4 data points for each incident) my $seen; for my $arr2 (@arr2) { if ( $arr2->[3] ) { # ie, if there's a complete call record print "@$arr2\n"; $seen = $arr2->[0]; print "\tDEBUG: My \$seen: $seen\n\n"; } for my $arr (@arr) { if ( ( $arr->[0] ) && ( $seen == $arr->[0] ) ) { print "@$arr\n"; } } print "\n\t\tFF Array exhausted for Callno $seen\n"; next; } print "\n\t\t No more calls\n";
Re: Iterating two co-dependant arrays
by gmargo (Hermit) on Jan 07, 2010 at 18:50 UTC

    I would divvy the arrays up into records separately from the matching part.

    # Divide @arr into records, store in %harr. my %harr; my $j = 0; while ($j+2 < scalar(@arr)) { my $rec = [ @arr[$j .. $j+2] ]; push @{$harr{ $rec->[0] }}, $rec; $j += 3; } # Divide @arr2 into records, store in %harr2. my %harr2; $j = 0; while ($j+3 < scalar(@arr2)) { my $rec = [ @arr2[$j .. $j+3] ]; push @{$harr2{ $rec->[0] }}, $rec; $j += 4; } # Munge foreach my $key (sort {$a<=>$b} keys %harr2) { print "@$_\n" foreach @{$harr2{$key}}; print "@$_\n" foreach @{$harr{$key}}; print "\n"; }