Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

See if arrays match

by Anonymous Monk
on Jul 23, 2002 at 21:23 UTC ( [id://184602]=perlquestion: print w/replies, xml ) Need Help??

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

Say I have a @list, how can I see if any of the items in list equal each other, but I dont know there position. I need to give an error if they do cause I have this code
$b = 0; while ( @slog = $sth->fetchrow_array ) { $b++; $info = param($b); ($a,$b) = split(/=/, $info); push(@info,$b) }

Replies are listed 'Best First'.
Re: See if arrays match
by PhiRatE (Monk) on Jul 23, 2002 at 22:00 UTC
    $b = 0; while ( @slog = $sth->fetchrow_array ) { $b++; $info = param($b); ($a,$b) = split(/=/, $info); push(@info,$b) }

    nononono..no..nono..

    I'm not yet entirely sure what you're trying to achieve by that code, but I would seriously consider re-writing it. You don't *appear* to have used 'strict' or 'warnings', and from the looks of things its a CGI script and you don't have 'taint' enabled either. These are important things, you haven't used my() to scope any of the variables and this will cause problems later on when you accidentally overwrite the $b somewhere else.

    Secondly you use fetchrow_array. This is fine *if* your SQL statement is carefully constructed so that you have specified the order or the fields coming out. If you've just used "select * ..." then you're in trouble if you ever alter your table schema. Instead consider fetchrow_hashref which will give you nicely named fields ($slog->{'row_id'} ..).

    Thirdly you're doing something really strange with $b there. Your first step is to set up $b as a counter, ok, thats cool. Then for each row in the result you increment $b (as if it were a counter), use $b as a param() reference, obviously to get some number-named form element (thats fine), but then weirdly you over-write $b with the value after the = sign in the split and push it on to an array (that hasn't been visibly initialised to empty or scoped but you may not have included that).

    In addition, you're using $info and @info in the same piece of code. Thats not good for readability at all.

    Now, back to your question, I'm assuming this need-to-know-about-duplicates is to do with the weirdness that is going on with $b. You want to error if you're likely to get into a loop with $b being the same value it was previously (bearing in mind that the loop will come to a halt anyway due to the rows of the query running out).

    The easiest way to do that is to use %info instead of @info and check it each time, as so:

    if (defined($info{$b})) { die "Duplicate reference"; } $info{$b} = $b;

    Then later when you need the list, use keys(). Alternatively if you need them in order, you would be advised either to use an OrderedHash module (there's one on CPAN) or an array and a hash, use the hash for checked and the array for order.

    To be honest, I think you would be best advised to instead post to perlmonks the entirety of your problem, rather than this little section. It seems like whatever it is, you're doing it the hard way and we may well be able to help more :)

Re: See if arrays match
by gryphon (Abbot) on Jul 23, 2002 at 21:29 UTC

    Greetings anonymous,

    The following was copied from the Perl Cookbook, Recipe 4.8 by Tom Christiansen & Nathan Torkington; ISBN 1-56592-243-3.

    @a = (1, 3, 5, 6, 7, 8); @b = (2, 3, 5, 7, 9); foreach $e (@a, @b) { $union{$e}++ && $isect{$e}++ } @union = keys %union; @isect = keys %isect;

    -gryphon
    code('Perl') || die;

      Looks like that only works if you are guaranteed that no items appear in the same list more than once. It would not work properly for this:
      @a = ( 1, 2, 3, 4, 2 );
      @b = ( 1, 3, 5 );

Re: See if arrays match
by Aristotle (Chancellor) on Jul 23, 2002 at 22:27 UTC

    Where's the @list in your code? Do you mean @info? PhiRatE makes good points about strict and warnings. Also, note that $a and $b, besides being obfuscatory names (what values are they supposed to be storing? the name does not explain their function), are special variables used by Perl for sort; using them is asking for trouble at some point down the road.

    All that said, the key to your problem, in one fashion or other, is to iterate over the list and store array elements as hash keys; as the value stored in the hash you will likely want a counter. If you are more specific about what you want, a more precise answer can be given.

    Makeshifts last the longest.

Re: See if arrays match
by flocto (Pilgrim) on Jul 23, 2002 at 23:15 UTC

    Since none of the above code examples quite statisfied me, here's my solution..

    # find duplicate entries in array @list my @sorted = sort (@list); my @duplicated; while (@sorted) { my $this = shift (@sorted); if ($this eq $sorted[0]) { push (@duplicated, $this); while (@sorted and $this eq $sorted[0]) { shift (@sorted); } } }

    There is potential to shorten up this code and make it more obfuscated, but for learning purposes this should be alright..

    Regards,
    -octo

      Your code returns the following when run with warnings enabled: Use of uninitialized value in string eq at... Here's one way to do avoid the warning:
      while ((my $this = shift @sorted) and @sorted) ...
      The warning occurs on the last iteration after you have shifted out the last element of @sorted.

      --Dave

        Oops, you're right.. I was in the unlucky position that the last element of my test array was a duplicate. In this case there wouldn't appear an error.. However, I think it's easier (to understand, anyway) to just change the condition for the first while to:

        while (@sorted > 1) ...

        Which works, because if there is only one element left, it can't be a duplicate.

        Thanks for pointing this out :)

        Regards,
        -octo

Re: See if arrays match
by ehdonhon (Curate) on Jul 23, 2002 at 21:31 UTC
    my %seen; foreach my $item ( @list ) { return error() if ( $seen{$item}++ ); }

    UPDATE No - that tells you if the item is in the same list twice. :(
    my %seen; $seen{@list} = (1) x @list; foreach my $item ( @list2 ) { return error() if ( $seen{$item} ); }
Re: See if arrays match
by kschwab (Vicar) on Jul 24, 2002 at 01:36 UTC
    I can't quite grok your question, but it sounds like you want to know the "intersection" of 2 arrays.

    From perldoc -q intersection:

    Found in /usr/lib/perl5/5.6.1/pods/perlfaq4.pod How do I compute the difference of two arrays? How do I compute the intersection of two arrays? Use a hash. Here's code to do both and more. It assumes that each element is unique in a given array: @union = @intersection = @difference = (); %count = (); foreach $element (@array1, @array2) { $count{$element}++ } foreach $element (keys %count) { push @union, $element; push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element; } Note that this is the symmetric difference, that is, all elements in either A or in B but not in both. Think of it as an xor operation.
Re: See if arrays match
by gmpassos (Priest) on Jul 24, 2002 at 03:41 UTC
    ## very very simple example...
    
    my @a = qw(0 1 2 3 a b c);
    my @b = qw(a b 0 1 3 c 2);
    
    if (join(";",sort @a) eq join(";",sort @b)) {
    print "OK\n" ;
    }
    
Re: See if arrays match
by mfriedman (Monk) on Jul 23, 2002 at 21:29 UTC
    Update

    Nevermind - gave the answer to the wrong question. :)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://184602]
Approved by Aristotle
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-04-25 17:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found