Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Get All Duplicated Elements in an Array (Once, without shifting)

by George_Sherston (Vicar)
on Oct 14, 2002 at 16:18 UTC ( [id://205137]=perlquestion: print w/replies, xml ) Need Help??

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

How can I extract just the unique elements of an array? is very helpful if that's what I want to do. But I need a list of all the elements that are *not* unique. And I need each element in this output list to be, itself, unique, even if it occurs more than twice in the input list. And to make matters worse, I need to do this in a for loop where I do other operations on the input list, so that shifting off one element after another is not an option. What I have at present is
my @in = qw/ test foo test bar baz foo test /; my %multiples; my %out; for (@in) { if ($multiples{$_} == 1) { $out{$_} = 1; } $multiples{$_} = 1; } print "$_\n" for keys %out;
This does what I want, but seems clunky. Is there a cute idiom for this operation?

§ George Sherston

Replies are listed 'Best First'.
•Re: Get All Duplicated Elements in an Array (Once, without shifting)
by merlyn (Sage) on Oct 14, 2002 at 16:19 UTC
Re: Get All Duplicated Elements in an Array (Once, without shifting)
by John M. Dlugosz (Monsignor) on Oct 14, 2002 at 19:47 UTC
    You could refine that to have an out list rather than an out hash. Every value you store in it has the value 1!

    my %multiples; my @out; for (@in) { if ($multiples{$_} == 1) { push @out, $_; } $multiples{$_} = 1; } print "@out";
    Now since it contains only one statement, you could write the if in suffix form. You could use ++ instead of setting the count to 1 in the unconditional statement.

    That gets down to:

    my %multiples; my @out; for (@in) { push @out, $_ if ($multiples{$_}++ == 1) } print "@out";
    That's tight enough that you can see how merlyn's form works. Just use the built-in looping mechanism of grep instead of your own foreach loop/push. It's the same thing.

    He used pre-increment == 2, which you'll note is the same thing as post-increment == 1. The latter is more directly equivilent to what you had originally, but the pre-increment is arguably more efficient (though I don't know if measurably faster in Perl).

Re: Get All Duplicated Elements in an Array (Once, without shifting)
by Flexx (Pilgrim) on Oct 15, 2002 at 00:02 UTC

    Of course, ++merlyns post shows what an elegant solution is... So that's the cute idiom you where asking for!

    Assuming you /can|want/ not process the duplicates right away, you could save memory by omitting the extra array @out. When you go over the array for the fist time, do the counting, then do the processing separately:

    my @in = qw(test foo test bar baz foo test); my %keycount; foreach (@in) { $keycount{$_}++; # do whatever else you need to do } foreach (keys %keycount) { #print $_ if $keycount > 1; print if $keycount{$_} > 1; # Thx, ++Aristotle! # or do something else with $_ }

    So long,
    Flexx

      That is, of course, print if $keycount{$_} > 1;

      Makeshifts last the longest.

        Yikes!!! Caught me with my pants way down... (you caught me back, I guess ;)

        But, it works if there are no duplicates! Umm... 8)

        Cheers,
        Flexx

        Update: struck a potentially offensive joke, that wasn't meant to be such.

Re: Get All Duplicated Elements in an Array (Once, without shifting)
by ehdonhon (Curate) on Oct 14, 2002 at 22:31 UTC
    my @in = qw/ test foo test bar baz foo test /; my %multiples; for (@in) { $multiples{$_}++; } for keys( %multiples ) { print "$_\n" if (--$multiples{$_}); }
Re: Get All Duplicated Elements in an Array (Once, without shifting)
by thor (Priest) on Oct 15, 2002 at 00:44 UTC
    Maybe I'm missing something, but...
    for(@in){ $hash{$_}++; } foreach my $key (keys %hash){ print "$key is lonely\n" if ($hash{$key} == 1); print "$key has friends\n" if ($hash{$key} != 1); }

    thor

    Update: changed %hash{$_}++ to $hash{$_}++ to make it correct.

      Well, the one thing you're missing is that %hash{$_}++ is illegal syntax, unless you've already projected forward about three years and are using Perl 6.

      -- Randal L. Schwartz, Perl hacker

        While it is true that %hash{$_}++ is illegal syntax, it is interesting to note that %hash->{$_}++is not. (But it only works on actual hashes and not hash references.) AFAIK its a bug, but one that has basically become a feature.
        use Data::Dumper; use strict; use warnings; my %hash; %hash->{$_}=$_ foreach 0..5; print Dumper(\%hash); __END__ $VAR1 = { '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5 };
        It embarrassed the crap out of me when I discovered this. I was reviewing some code of my colleagues, (relatively new to perl at the time) and identified these as compile time errors. He politely told me what I was full of :-) and then showed me it compiled (and worked) fine. Luckily he had a few other subtle bugs that I found so I managed to avoid looking like a complete moron. :-)

        --- demerphq
        my friends call me, usually because I'm late....

        How much of an estimate is 'about' and are you still confident with your prediction? :P

        -=( Graq )=-

      A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (5)
As of 2024-04-23 15:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found