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

Hello monks!
If you have the following AoA:
@AoA = ( [ "X", "Z", "W", "X", "Z", "Z" ], [ "Z", "Z", "X", "X", "X", "W" ], [ "X", "Z", "X", "W", "W", "W" ], [ "Z", "X", "W", "X", "Z", "Z" ], [ "Z", "X", "W", "X", "X", "W" ], [ "Z", "X", "X", "X", "Z", "W" ], );
is there a way to traverse this AoA by column? I want to calculate the number of occurences for each letter (X, W and Z) in the respective column, and, if there are more than 60% of one letter in a column, the the final combined array will hold this letter. If we have a tie, or les than 60%, we would use another letter, say A
In particular, the combined array that would come up from this AoA would be:
[ "Z", "A", "A", "X", "Z", "W" ]

Replies are listed 'Best First'.
Re: Find all values of an AoA by columns
by ikegami (Patriarch) on Jun 11, 2009 at 19:33 UTC
    my $num_cols = @{ $AoA[0] }; for my $col (0..$num_cols-1) { for my $row (0..$#AoA) { ... } }
    or
    my $num_cols = @{ $AoA[0] }; for my $col (0..$num_cols-1) { my @col = map $_->[$col], @AoA; ... }

    This is really the same question as Can't think of an algorithm to go...

    sub consensus { my %counts; $counts{$_}++ for @_; my ($consensus) = sort { $counts{$b} <=> $counts{$a} } keys(%counts); return ( $counts{$consensus} / @_ >= 0.6 ? $consensus : 'A' ); } { my $num_cols = @{ $AoA[0] }; my @combined; for my $col (0..$num_cols-1) { push @combined, consensus( map $_->[$col], @AoA ); } print(join(', ', @combined), "\n"); }

    Updated

Re: Find all values of an AoA by columns
by GrandFather (Saint) on Jun 11, 2009 at 20:46 UTC

    The traversal is easy as already shown. The next step is to build a list of counts. Consider:

    use strict; use warnings; use Data::Dump::Streamer; my @AoA = ( [ "X", "Z", "W", "X", "Z", "Z" ], [ "Z", "Z", "X", "X", "X", "W" ], [ "X", "Z", "X", "W", "W", "W" ], [ "Z", "X", "W", "X", "Z", "Z" ], [ "Z", "X", "W", "X", "X", "W" ], [ "Z", "X", "X", "X", "Z", "W" ], ); my @sums; for my $row (0 .. $#AoA) { for my $col (0 .. $#{$AoA[$row]}) { ++${$sums[$col]}{$AoA[$row][$col]}; ++${$sums[$col]}{total}; } } Dump \@sums;

    Prints:

    $ARRAY1 = [ { total => 6, X => 2, Z => 4 }, { total => 6, X => 3, Z => 3 }, { total => 6, W => 3, X => 3 }, { total => 6, W => 1, X => 5 }, { total => 6, W => 1, X => 2, Z => 3 }, { total => 6, W => 4, Z => 2 } ];

    'total' isn't required if there are no missing rows, but may be a good sanity check. The remainder of the solution is left as an exercise for the reader.

    BTW: you can't have a tie with the threshold at 60%, it must be 50% or less. Indeed your sumple supports 50% or there wouldn't be a Z for the penultimate column.


    True laziness is hard work
Re: Find all values of an AoA by columns
by Transient (Hermit) on Jun 11, 2009 at 19:47 UTC
    In the case where the rows didn't all have the same number of columns:
    my $col = 0; while ( my @col_values = map { $_->[$col] } @AoA ) { last unless grep /^.$/, @col_values; print join (",", @col_values), "\n"; $col++; }
    Although I'm sure there is a more elegant way to write it.
Re: Find all values of an AoA by columns
by NetWallah (Canon) on Jun 11, 2009 at 21:22 UTC
    use strict; my @AoA = ( [ "X", "Z", "W", "X", "Z", "Z" ], [ "Z", "Z", "X", "X", "X", "W" ], [ "X", "Z", "X", "W", "W", "W" ], [ "Z", "X", "W", "X", "Z", "Z" ], [ "Z", "X", "W", "X", "X", "W" ], [ "Z", "X", "X", "X", "Z", "W" ], ); my @col; for my $row (@AoA){ $col[$_]{$row->[$_]}++ for 0..$#{$row}; } for my $v(@col){ my ($first,$second)= sort {$v->{$b} <=> $v->{$a}} keys %$v ; print ($v->{$first} eq $v->{$second} ? "A":$first, " "); } print "\n";
    Prints:
    Z A A X Z W

    Update: Not sure what you meant by the 60% - perhaps this print replacement :

    print ($v->{$first} == $v->{$second} || $v->{$first} < 0.6 * scalar(keys %$v) ? "A": $first, " ");

         Potentia vobiscum ! (Si hoc legere scis nimium eruditionis habes)

Re: Find all values of an AoA by columns
by repellent (Priest) on Jun 11, 2009 at 21:36 UTC
    Others have mentioned how to traverse by column. Here's how to transpose @AoA using map:
    my @AoA_transposed = map { my $y = $_; [ map { $AoA[$_][$y] } 0 .. $#AoA ] } 0 .. $#{ $AoA[0] };
Re: Find all values of an AoA by columns
by lostjimmy (Chaplain) on Jun 11, 2009 at 20:42 UTC
    There is also Array::Transpose, if you're looking for a module to do it for you. However, it's really just the map solution that ikegami already mentioned.
Re: Find all values of an AoA by columns
by bichonfrise74 (Vicar) on Jun 11, 2009 at 22:04 UTC
    Another possible and less elegant way of traversing the column...
    #!/usr/bin/perl use strict; my @results; my @AoA = ( [ "X", "Z", "W", "X", "Z", "Z" ], [ "Z", "Z", "X", "X", "X", "W" ], [ "X", "Z", "X", "W", "W", "W" ], [ "Z", "X", "W", "X", "Z", "Z" ], [ "Z", "X", "W", "X", "X", "W" ], [ "Z", "X", "X", "X", "Z", "W" ], ); for my $i ( 0 .. $#AoA ) { my %count; for my $j ( 0 .. scalar( @{ $AoA[0] } ) - 1) { $count{ $AoA[$j]->[$i] }++; } for my $k ( reverse sort { $count{$a} <=> $count{$b} } keys %count ) { print "$k\n"; last; } } print join ", ", @results;