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

Dear all, I would like to make a table where all of the elements in my array are written out pairwise (no self-self pairs, and no reverse pairs (ie. only A-B and not A-B, B-A). I thought that the code below would do this, but it is printing some self pairs and reverse pairs. any help is appreciated.

foreach my $pwy (keys %HoPwy){ my @members=@{$HoPwy{$pwy}}; my $pwyLen=scalar(@members); for (my $i=0;$i<($pwyLen-1);$i++){ for (my $j=1;$j<$pwyLen;$j++){ print OUT join("\t",$members[$i],$memb +ers[$j],$pwy)."\n"; } } } close(OUT);

Replies are listed 'Best First'.
Re: writing array as pairwise
by Eily (Monsignor) on Jan 23, 2015 at 15:46 UTC

    As GotToBTru said, your code isn't very useful without sample data.

    But if I have understood your problem well, there is one obvious solution. Uniqueness means hash:

    sub existsPair { our %pairs; my (@pair) = sort @_; local $" = "~~"; # Only works as long as ~~ can't appear in one of t +he inputs values return $pairs{"@pair"}++; } existsPair "Hello", "World"; # false existsPair "Hello", "World"; # true existsPair "World", "Hello"; # true existsPair "He", "lloWorld"; # false
    The hardest part of the job is to find a way to stringify the pair in a way that is actually unique, it can be by using an invalid character as a separator, or using pack for prepending the size of the first string, or whatever works.

Re: writing array as pairwise
by sn1987a (Curate) on Jan 23, 2015 at 16:20 UTC
    Your problem is with the nested $j loop. The starting condition should be $j=$i+1. Otherwise you will generate both [1][2] and [2][1].

      That was it. Thanks. Sorry I didn't properly explain my request. basically, if I have an array A,B,C,D. I want to print out AB AC AD BC BD CD

        Oh. See also Algorithm::Loops, Algorithm::Permute (and others, I'm sure). Also, an iterator approach:

        c:\@Work\Perl>perl -wMstrict -le "sub Iterator (&) { return $_[0]; } ;; sub pairs { my @set = @_; my ($i, $j) = (0, 0); ;; return Iterator { $j = ++$i + 1 if ++$j > $#set; ($i, $j) = (0, 0) and return unless $i < $#set; return [ @set[ $i, $j ] ]; }; } ;; my $abcd = pairs(qw(a bee c d)); while (my $p = $abcd->()) { printf qq{(@$p) }; } print ''; ;; while (my $p = $abcd->()) { printf qq{(@$p) }; } " (a bee) (a c) (a d) (bee c) (bee d) (c d) (a bee) (a c) (a d) (bee c) (bee d) (c d)


        Give a man a fish:  <%-(-(-(-<

Re: writing array as pairwise
by LanX (Saint) on Jan 23, 2015 at 15:50 UTC
    Either copy to a new array and shift twice to get all "pairs"

    or iterate an index with step 2 and print a array slice

    @member[$i,$i+1]

    But your use of nested loops doesn't make sense, could be that your problem description is misleading.

    Tip: providing input and desired output data helps if you can't express yourself.

    Cheers Rolf

    PS: Je suis Charlie!

Re: writing array as pairwise
by AnomalousMonk (Archbishop) on Jan 23, 2015 at 18:56 UTC

    I, too, am guessing a bit about what you want and about just what your input data may look like, but here's another example of what some of the other monks are talking about:

    c:\@Work\Perl>perl -wMstrict -MData::Dump -le "my %HoPwy = ( foo => [ qw(abe lincoln john doe freddy mac) ], bar => [ qw(homer simpson krazy kat) ], ); dd \%HoPwy; ;; for my $pwy (keys %HoPwy){ my $pwy_arrayref = $HoPwy{$pwy}; for (my $i = 0, my $j = 1; $i < $#$pwy_arrayref; $i += 2, $j += 2 +) { print qq{'$pwy': '$pwy_arrayref->[$i]', '$pwy_arrayref->[$j]'}; } } " { bar => ["homer", "simpson", "krazy", "kat"], foo => ["abe", "lincoln", "john", "doe", "freddy", "mac"], } 'bar': 'homer', 'simpson' 'bar': 'krazy', 'kat' 'foo': 'abe', 'lincoln' 'foo': 'john', 'doe' 'foo': 'freddy', 'mac'


    Give a man a fish:  <%-(-(-(-<

Re: writing array as pairwise
by GotToBTru (Prior) on Jan 23, 2015 at 15:11 UTC

    Some sample data would make this easier to understand.

    Dum Spiro Spero
Re: writing array as pairwise
by karlgoethebier (Abbot) on Jan 24, 2015 at 12:40 UTC

    Another solution using List::MoreUtils:

    #!/usr/bin/env perl use strict; use warnings; use List::MoreUtils qw(natatime); use Data::Dump; my %HoPwy = ( foo => [qw(abe lincoln john doe freddy mac)], bar => [qw(homer simpson krazy kat)], ); for my $pwy ( sort keys %HoPwy ) { # my $pwy_arrayref = $HoPwy{$pwy}; # my $iterator = natatime 2, @$pwy_arrayref; my $iterator = natatime 2, @{ $HoPwy{$pwy} }; while ( my @pairs = $iterator->() ) { dd $pwy, \@pairs; } } __END__ karls-mac-mini:monks karl$ ./1114295.pl ("bar", ["homer", "simpson"]) ("bar", ["krazy", "kat"]) ("foo", ["abe", "lincoln"]) ("foo", ["john", "doe"]) ("foo", ["freddy", "mac"])

    N.B.: Some variables names stolen from Re: writing array as pairwise by AnomalousMonk.

    Update: ...shortened code a bit.

    Best regards, Karl

    «The Crux of the Biscuit is the Apostrophe»