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

Perl newbie here… need some help and suggestions. I’ve searched for many examples but so far it’s all for array operation for two arrays. I have three arrays and want to find the common value in the three array and list them out :

Array 1 = {0, 1,2,3,4,5,6,7,8,9} Array 2 = {1,2,3,4,6,8, 10, 12,14} Array 3 = {1,2,3,5,7,9, 11,13,15}

I want the output to be something like below:

Array 1 Array 2 Array 3 0 yes 1 yes yes yes 2 yes yes yes 3 yes yes yes 4 yes yes 5 yes yes 6 yes yes 7 yes yes 8 yes yes 9 yes yes 10 yes 11 yes 12 yes 13 yes 14 yes 15 yes

The idea is to list out the output as above. I’ve started with something below which I got from another website but got stuck :

#!/usr/bin/perl use strict; use warnings; my @array1; my @array2; my @diff; my @isect; my $item; my %count; @array 1 = (0, 1,2,3,4,5,6,7,8,9); @array 2 = (1,2,3,4,6,8, 10, 12,14); @isect = ( ); @diff = ( ); %count = ( ); foreach $item (@array1, @array2) { $count{$item}++;} foreach $item (keys %count) { if ($count{$item} == 2) { push @isect, $item; } else { push @diff, $item; } } print "\nA Array = @array1\n"; print "\nB Array = @array2\n"; print "\nIntersect Array = @isect\n"; print "\nDiff Array = @diff\n\n";

Appreciate the help

Replies are listed 'Best First'.
Re: Array comparison for 3 arrays
by GrandFather (Saint) on Apr 14, 2010 at 04:24 UTC

    Telling us where you got stuck may help.

    I see for a start that you seem to be trying to use variable names (for the arrays) that include spaces. Perl doesn't let you do that and will tell you off for it (although the error message may not be entirely clear).

    If you want to deal with several 'things' you have a few choices. You could use a separate variable for each (your current approach), but that quickly becomes hard to manage, or you can use an array or a hash to organise the 'things'. If you want to deal with them by name a hash is the right beastie otherwise an array may be more appropriate.

    By the look of your current code it seems you probably want to deal with the arrays by name so a hash is the appropriate variable to manage your collection of 'things' (arrays). Consider:

    #!/usr/bin/perl use strict; use warnings; use List::Util; # Read the lists of numbers my %lists; my $nameWidth = 5; my $maxNum = 0; # Implicit assumption that minimum value is 0 while (<DATA>) { next if ! /^([^=]+)\ =\s* {([^}]+)/x; my ($name, $list) = ($1, $2); my @values = split /\s*,\s*/, $list; $name =~ s/^\s+|\s+$//g; # Trim leading and trailing white space $lists{$name} = {map {$_ => 1} @values}; $nameWidth = length ($name) + 1 if $nameWidth <= length $name; $maxNum = List::Util::max ($maxNum, @values); } my @names = sort keys %lists; my $template = "-%4s " . ("%-${nameWidth}s" x @names) . "\n"; printf $template, '', @names; for my $row (0 .. $maxNum) { my @values; for my $name (@names) { my $list = $lists{$name}; push @values, exists $list->{$row} ? 'yes' : ''; } printf $template, $row, @values; } __DATA__ Array 1 = {0, 1,2,3,4,5,6,7,8,9} Array 2 = {1,2,3,4,6,8, 10, 12,14} Array 3 = {1,2,3,5,7,9, 11,13,15}

    Prints:

    Array 1 Array 2 Array 3 0 yes 1 yes yes yes 2 yes yes yes 3 yes yes yes 4 yes yes 5 yes yes 6 yes yes 7 yes yes 8 yes yes 9 yes yes 10 yes 11 yes 12 yes 13 yes 14 yes 15 yes

    There is quite a lot going on in there so you are going to need to study the code for a while and consult the Perl documentation. You may even need to come back and ask some more questions, but try to figure it out for yourself for a start and do read the documentation!

    True laziness is hard work
Re: Array comparison for 3 arrays
by ikegami (Patriarch) on Apr 14, 2010 at 05:11 UTC
    • You need to find the value at which to start.
    • You need to find the value at which to stop.
    • You need some way of determining if a value is present in an array. (I used a hash rather than grepping the entire array over and over again.)
    use strict; use warnings; my @arrays = ( [0,1,2,3,4,5,6,7,8,9], [1,2,3,4,6,8,10,12,14], [1,2,3,5,7,9,11,13,15], ); my $min = my $max = $arrays[0][0]; my @lkups; for (0..$#arrays) { my $array = $arrays[$_]; my $lkup = \%{ $lkups[$_] }; for (@$array) { $min = $_ if $_ < $min; $max = $_ if $_ > $max; ++$lkup->{$_}; } } for my $i ($min..$max) { print(join("\t", $i, map { $_->{$i} ? 'yes' : '' } @lkups), "\n"); }

    Alternatively, you could take advantage of the fact that the numbers are sorted and do something like a merge sort.

    Update: Oops, I thought you wanted continuous indexes, but I doubt that now. Fix:

    • You need to find which values are present in any array.
    • You need some way of determining if a value is present in an array. (I used a hash rather than grepping the entire array over and over again.)
    use strict; use warnings; my @arrays = ( [0,1,2,3,4,5,6,7,8,9], [1,2,3,4,6,8,10,12,14], [1,2,3,5,7,9,11,13,15], ); my %global_lkup; my @lkups; for (0..$#arrays) { my $array = $arrays[$_]; my $lkup = \%{ $lkups[$_] }; for (@$array) { ++$lkup->{$_}; ++$global_lkup{$_}; } } for my $i ( sort { $a <=> $b } keys(%global_lkup) ) { print(join("\t", $i, map { $_->{$i} ? 'yes' : '' } @lkups), "\n"); }
Re: Array comparison for 3 arrays
by colwellj (Monk) on Apr 14, 2010 at 03:44 UTC
    You could try an array of arrays; So;
    #!/usr/bin/perl -w use strict; my @array2; my @array1; my @array3; my @diff; @array1 = (0,1,2,3,4,5,6,7,8,9); @array2 = (1,2,3,4,6,8,10,12,14); @array3 = (1,2,3,5,7,9,11,13,15); foreach(@array1){ $diff[$_][0] = "yes"; } foreach(@array2){ $diff[$_][1] = "yes"; } foreach(@array3){ $diff[$_][2] = "yes"; } print "\tArray1\tArray2\tArray3"; for(0..$#diff){ print "\n$_"; foreach(@{$diff[$_]}){ if(defined($_)){ print "\t$_"; }else{ print "\t"; } } } print "\n";
    Code produces the following.
    Array1 Array2 Array3 0 yes 1 yes yes yes 2 yes yes yes 3 yes yes yes 4 yes yes 5 yes yes 6 yes yes 7 yes yes 8 yes yes 9 yes yes 10 yes 11 yes 12 yes 13 yes 14 yes 15 yes
Re: Array comparison for 3 arrays
by rovf (Priest) on Apr 14, 2010 at 07:32 UTC
    You could build a hash, where the keys are the numbers in the array, and the values encode, which arrays those number are located in.

    -- 
    Ronald Fischer <ynnor@mm.st>
Re: Array comparison for 3 arrays
by Khen1950fx (Canon) on Apr 14, 2010 at 07:22 UTC
    I can see what you mean. Most of the examples show two arrays, but trying it with three arrays like this works:
    #!/usr/bin/perl use strict; use warnings; use Set::Intersection; my @arr1 = qw/0 1 2 3 4 5 6 7 8 9/; my @arr2 = qw/1 2 3 4 6 8 10 12 14/; my @arr3 = qw/1 2 3 5 7 9 11 13 15/; my @intersection = get_intersection(\@arr1, \@arr2, \@arr3); print reverse (@intersection), "\n";
    It returns 123.
Re: Array comparison for 3 arrays
by PeterPeiGuo (Hermit) on Apr 14, 2010 at 05:49 UTC

    Essentially you want to transform the original data structure into something that can be easily iterated and displayed, in the way you wanted.

    use strict; use warnings; use Data::Dumper; my $arrays = {"a1" => [0, 1,2,3,4,5,6,7,8,9], "a2" => [1,2,3,4,6,8, 10, 12,14], "a3" => [1,2,3,5,7,9, 11,13,15]}; my $result = {}; foreach my $array_name (keys %$arrays) { map {$result->{$_}->{$array_name} = 1} @{$arrays->{$array_name}} } print Dumper($result);

    Peter (Guo) Pei

      Do not use map in void context. Although it probably doesn't generate the output list in current versions of Perl, it does send wrong signals about the intent of the code. Use for as a statement modifier instead:

      $result->{$_}{$array_name} = 1 for @{$arrays->{$array_name}}
      True laziness is hard work

        Don't use postfix for. :)

        my @array = $arrays->{$array_name}; for my $number (@array) { $result->{$number}->{$array_name} = 1 }
Re: Array comparison for 3 arrays
by rovf (Priest) on Apr 14, 2010 at 07:38 UTC

      roolic's answer is flawed. Building a hash is smart, but building it over and over again is dumb. Even

      sub exists { my ($array, $value) = @_; return grep $_ eq $value, @$array; } exists( $arr_ref, $value ) ? 'yes' : ''
      would be better than
      sub exists { my ($array, $value) = @_; my %all_vals = map { $_=>1 } @{$array}; return $all_vals{$value}; } exists( $arr_ref, $value ) ? 'yes' : ''

      Hi all, thanks for all your input, help and suggestions. Although some of the examples are difficult to understand but I appreciate the effort... I'm beginning to get the idea and concept and will do some study on some these examples given here...

      The reason I cross post to a few forums is I will get as many examples as possible with one that I hopefully will understand... Will take note of that in the future.

      Thanks again all

        The reason I cross post to a few forums is I will get as many examples as possible
        This is of course the only reason why people crosspost. But if you do this, you should at the very least include in your posting where you crosspost.

        -- 
        Ronald Fischer <ynnor@mm.st>