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

Hi,

I'm trying to look through a two dimensional array and indicate all repeating elements. I'm sure there is some clever way to do it with two lines of code, but I can't think of one.

If I have an array that looks like:

@all=(["what", "is", "the", "matrix"],["matrix", "reloaded", "is", "no", "good"]);

I'd like to have an array which looks like:

([0, 1, 0, 1], [1, 0, 1, 0, 0])

So that all matches are pointed out. What would be the best way to do this?

p.s. a simple but comprehensive explanation of the map function will also be appreciated.

Replies are listed 'Best First'.
Re: two dimensional array lookup
by simonm (Vicar) on Jul 17, 2003 at 04:32 UTC

    Two lines are indeed enough.

    # Build a hash of word counts foreach ( map @$_, @all ) { $words{$_} ++ } # Create a list of arrays showing repeats @out = map { [ map $words{$_} > 1 ? 1 : 0, @$_ ] } @all;

    With regards to map, I find it easiest to think of it as a rolled-up form of the common foreach ... push idiom:

    # List processing with foreach/push foreach ( @input ) { push @out, lc($_); } print @out; # Equivalent "rolled-up" map expression: print map { lc($_) } @input;
      ++ because It took me some time to figure out how your solution works.
Re: two dimensional array lookup
by sgifford (Prior) on Jul 17, 2003 at 04:35 UTC
    The most straightforward way to do what you want is to use a hash to keep track of the elements that already exist, then scan the hash to determine duplicates. Something like:
    #!/usr/bin/perl -w use strict; use vars qw(@all @dup); @all=(["what", "is", "the", "matrix"],["matrix", "reloaded", "is", "no +", "good"]); my(%h1,%h2); # for the two halves of your array grep { $h1{$_}=1 } @{$all[0]}; grep { $h2{$_}=1 } @{$all[1]}; @dup = ( [ map { $h2{$_} ? 1 : 0 } @{$all[0]} ], [ map { $h1{$_} ? 1 : 0 } @{$all[1]} ]); print '([', join(',',@{$dup[0]}), '], [', join(',',@{$dup[1]}), "])\n";
    Oh, and map executes a block of code for each element in the array, returning an array consisting of the return value from each block of code.
      I would write this
      grep { $h1{$_}=1 } @{$all[0]};
      like this
      grep { $h1{$_}=1; undef; } @{$all[0]};
      in order not to get all my elements from @{$all[0]} as a return value of grep.

      I even might write:

      @h1{@{$all[0]}}=(1) x @{$all[0]};
        Why use grep at all if you don't need a return value?
        $h1{$_}=1 for @{$all[0]};
Re: two dimensional array lookup
by tos (Deacon) on Jul 17, 2003 at 13:55 UTC
    when i saw simonms great solution i was ashamed of my own. But since it was a little trouble for me and because of TMTOWTDI here my approach.

    use strict; use warnings; use Data::Dumper; my @erg; my @all=(["what", "is", "the", "matrix"],["matrix", "reloaded", "is", +"no", "good"]); print "\n",Dumper(\@all); for (my $i = 0; $i < @{$all[0]}; $i++) { for (my $j = 0; $j < @{$all[1]}; $j++) { $erg[0][$i] ||= sprintf "%u", ($all[0][$i] eq $all[1][$j]); $erg[1][$j] ||= sprintf "%u", ($all[0][$i] eq $all[1][$j]); } } print "\n",Dumper(\@erg);
    greetings, tos