Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Finding Redundant values in arrays

by Anonymous Monk
on Jun 07, 2001 at 23:55 UTC ( #86703=perlquestion: print w/ replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to figure out a way to take a array like this:
@test = ("camel","aplaca","peguin","aplaca","monkey","camel","camel");
and get a new array with with all the values of the array that were redundant so I would get:
@redundant = ("camel","aplaca");
How can I do this???

Comment on Finding Redundant values in arrays
Select or Download Code
Re: Finding Redundant values in arrays
by bikeNomad (Priest) on Jun 07, 2001 at 23:59 UTC
    You can count occurrences using a hash:

    @test = ("camel","alpaca","penguin","alpaca","monkey","camel","camel") +; my %counts; $counts{$_}++ foreach @test; @redundant = grep { $counts{$_}>1 } keys(%counts);

    And please note that camels are never redundant <g>.

    update: changed map to foreach so as to avoid the dreaded map in void context

      or, for slightly better performance (IIRC), ditch map in void context, and use

      $counts{$_}++ for @test;
      or (for us antediluvian 5.004 types)
      for (@test) {$counts{$_}++}

      I was hoping there was a Schwartzian Transform analog for this, just for the fun of running the whole thing into one line, but I can't come up with one.

      Update:
      No sooner posted than obsolete...

      @redundant = grep {++$counts{$_} > 1} @test;
      Oops!
      @redundant = grep {++$counts{$_}==2} @test;
      and a pox on foolish monks who post without full testing...



      If God had meant us to fly, he would *never* have give us the railroads.
          --Michael Flanders

(zdog) Re: Finding Redundant values in arrays
by zdog (Priest) on Jun 08, 2001 at 00:01 UTC
    Updated with info from below, but still an incorrect answer:

    for my $test (@test) { push (@redundant, $test) unless grep { $test eq $_ } @redundant; }

    Zenon Zabinski | zdog | zdog7@hotmail.com

      This won't work. The for (@test) line iterates through each element of @test, but then you skip each element with the grep you're using. In English you're saying: for each element in the array, only keep that element if it is not in the array.

      Also, there is no need to place quotes round $_.

      Update: This is wrong. dvergin explains what really happens in Re: (zdog) Re: Finding Redundant values in arrays.

      Oops! You are mistaken...
      my @test = qw(camel aplaca peguin aplaca monkey camel camel"); my @redundant; for (@test) { next if grep ("$_", @redundant); push (@redundant, $_); } print "@redundant\n";
      Prints: camel

      Update: zdog asked about this in a private CB message so here goes. tomhukins is not quite correct in his description of the problem. What happens is this:

      grep ("$_", @redundant) in a scalar context returns the number of times the expression $_ is true for all the elements of @redundant. But the first time through, there are no elements in @redundant so $_ is evaluated zero times.

      So the first time through the loop, the "next if" fails and "camel" is pushed onto @redundant. Each time through the loop after than, @redundant does contain an element and the grep in a scalar context returns 1 meaning "for the one element in @redundant, $_ (the element 'camel') is true."

      (Note: I am aware of the discussion about whether to say that grep always returns a list but that a list in scalar context evaluates to the number of elements or whether to say that grep in a scalar context returns a number. perlfunc says: "In scalar context, [grep] returns the number of times the expression was true.")

        This is broken, notice that it just prints the first element of the @test array? So aplaca doesn't print. In fact if you put peguin as the first element of test it prints peguin. The problem is that you're trying to be to terse. The $_ that you're using in the grep is not the $_ that aliases an element of @test, it's the $_ that aliases an element of @redundant (grep uses $_ too). So you're saying next if there's an element of redundant that matches an element of redundant. Probably not really what your were thinking of.

        Update: Well, it was broken. I started getting down votes for this node, I guess 'cause it's no longer applicable.

        :) Ira

        "So... What do all these little arrows mean?"
        ~unknown

Re: Finding Redundant values in arrays
by wog (Curate) on Jun 08, 2001 at 00:02 UTC

    my @test = qw( camel aplaca peguin aplaca monkey camel camel ); my %test; $test{$_}++ for @test; my @redundant; while (my($k,$v) = each %test) { push @redundant, $k if $v > 1; } print "@redundant\n";
Re: Finding Redundant values in arrays
by tomhukins (Curate) on Jun 08, 2001 at 00:03 UTC
Re: Finding Redundant values in arrays
by dvergin (Monsignor) on Jun 08, 2001 at 00:03 UTC
    TMTOWTDI:
    my @test = qw(camel aplaca peguin aplaca monkey camel camel); my %temp; my @redundant = grep $temp{$_}++ == 1, @test; print "@redundant\n";
    Update: Good catch, Wog. It works as expected now. I've corrected the line from:
    #my @redundant = grep $temp{$_}++, @test;
    I did test but didn't see the typo in converting to a qw(). Typing like 60 and missed the curve!

    Weak rejoinder: though not the spec for this problem, it would be useful in some circumstances. ;-)

      Although this code works as given it is not a working solution. If we remove the " from the 'camel"', then it prints camel twice. (And it would do it more times if there were a fourth camel in the array, etc.)
Re: Finding Redundant values in arrays
by tachyon (Chancellor) on Jun 08, 2001 at 00:08 UTC

    This does the trick using hashes

    tachyon

    my @test = ("camel","aplaca","peguin","aplaca","monkey","camel","camel +"); my (%duplicate,%test,@redundant); for (@test) { $duplicate{$_} = 1 if defined $test{$_}; $test{$_} = 1; } @redundant = keys %duplicate; print "@redundant\n";
Re: Finding Redundant values in arrays
by IraTarball (Monk) on Jun 08, 2001 at 00:18 UTC
    This is spun off of the Extracting Unique Elements from a List section of the Perl Cookbook.
    %seen = (); @unique = grep { ++$seen{$_} == 2} @test;
    If you're not familiar with grep you can read all about it in perldoc -f grep but as I understand it, it returns a list consisting only of the elements in @test for which the block or expression return true. So for this snippet the block { ++$seen{$_} == 2} only returns true the second time the element has been seen.

    Hope this helps
    Ira

    "So... What do all these little arrows mean?"
    ~unknown

Re: Finding Redundant values in arrays
by tachyon (Chancellor) on Jun 08, 2001 at 00:21 UTC

    If you prefer map you could:

    my @test = ("camel","aplaca","peguin","aplaca","monkey","camel","camel +"); my (%duplicate,%test,@redundant); map{exists$test{$_}?$duplicate{$_}=1:$test{$_}=1}@test; @redundant = keys %duplicate; print "@redundant\n";

    tachyon

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (4)
As of 2014-08-31 07:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (294 votes), past polls