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

Hi Monks,

UPDATE:

I have figured out what's the problem, I was using qw{1, 1} to construct the array, then the first element is "1," and the other one is "1", thanks for your attention, embarrassing...

I encountered a subroutine like the following:
sub remove_dup { my @arr = shift; my %seen; for ( my $i = 0; $i <= $#{$arr}; ){ splice @$arr, --$i, 1 if $seen{$arr->[ $i++]}++; } }

It intended to remove the duplicated elements in the array, but when the element is the last one in the array, it can't be removed?

just like {1,1} can't be changed to {1}, but {1,1,2} will be changed to {1,2}.

Can anyone give a hint?
  • Comment on Can't remove the the duplicate element when it's the last one in the array
  • Download Code

Replies are listed 'Best First'.
Re: Can't remove the the duplicate element when it's the last one in the array
by Ratazong (Monsignor) on Dec 18, 2009 at 09:03 UTC
    Your code seems to be too "tricky" (read: complicated)
    Especially, you should avoid changing variables more than once in a statement. E.g. using --$i and $i++ in
    splice @$arr, --$i, 1 if $seen{$arr->[ $i++]}++;

    As already recommended by WizardOfUz, you should use uniq(). If you want to do it all by yourself, the following code seems to work;
    sub remove_dup { my @arr = (1,1); #,2, 1, 3, 1, 4, 3, 1); my %seen; for ( my $i = 0; $i < scalar @arr; $i++){ splice @arr, $i--, 1 if $seen{$arr[ $i]}++; } print @arr, " "; }
      my %seen; for ( my $i = 0; $i < scalar @arr; $i++){ splice @arr, $i--, 1 if $seen{$arr[ $i]}++; }
      can be simplified to
      my %seen; for ( my $i = @arr; $i--; ){ splice @arr, $i, 1 if $seen{$arr[$i]}++; }
      But the following will be much faster:
      my %seen; @arr = grep !$seen{$_}++, @arr;

      Ratazong, I have figured out it's my mistake. Yes, It's more readable to write it in your style, many thanks to you.

Re: Can't remove the the duplicate element when it's the last one in the array
by WizardOfUz (Friar) on Dec 18, 2009 at 08:51 UTC

    Take a look at the uniq() function in List::MoreUtils for an elegant (perlish) solution.

Re: Can't remove the the duplicate element when it's the last one in the array
by AnomalousMonk (Archbishop) on Dec 18, 2009 at 11:06 UTC
    Take a look at the  uniq() function ...

    I second that.

    Your code seems to be too "tricky" ...

    Too tricky by half. But if you are going to recommend 'simple' code, at least recommend the idiomatic version:

    >perl -wMstrict -le "my @ra = (1, 3, 2, 3, 2, 3, qw{X b X c X}); my %seen; @ra = grep !$seen{$_}++, @ra; print qq{@ra}; " 1 3 2 X b c

      If order need not be preserved I think a hash slice is quicker.

      $ perl -MBenchmark=cmpthese -Mstrict -wE ' > my @arr; > push @arr, int rand 20 for 1 .. 1000; > cmpthese( > -5, > { > grep => sub > { > my %seen; > my @uniq = grep ! $seen{ $_ } ++, @arr; > }, > slice => sub > { > my %seen; > @seen{ @arr } = ( 1 ) x @arr; > my @uniq = keys %seen; > }, > } > );' Rate grep slice grep 3041/s -- -29% slice 4260/s 40% -- $

      However, take my benchmarks with a pinch of salt, I often make a complete dog's breakfast of them :-(

      I hope this is of interest.

      Cheers,

      JohnGG

        johngg, it looks interesting and helpful, I will have a try:)
      AnomalousMonk, your example are very helpful to me, thanks a lot!
Re: Can't remove the the duplicate element when it's the last one in the array
by JavaFan (Canon) on Dec 18, 2009 at 10:52 UTC
    I was using qw{1, 1} to construct the array,
    You do know that using qw{1, 1} triggers a warning, don't you?
      If littlehorse didn't know about the warning, it's most likely because he also doesn't know that this line should have been included somewhere near the top of script file:
      use warnings;
      I don't know that will trigger a warning, I'm new to Perl.

      I forget to use the -w switch when I use the Perl interpreter to execute the script. I will let that switch on next time, thanks.