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

Hi all, I know this question has been asked before. But what's different now, is I want to sort an AofA based on the uniqueness of 2 columns - all done without using Map or Grep. I have a simple code that can work on a 1-dimension array. Can someone advice on how I can modify it to do a sort based on 2 columns? Thanks.
my @array; push @{@array},["1", "1", "3"]; push @{@array},["1", "5", "6"]; push @{@array},["1", "1", "3"]; my @unique = (); my %seen = (); foreach my $elem ( @array) { next if $seen{ $elem }++; push @unique, $elem; } for my $i (0 .. $#unique) { print "\n$unique[$i][0] $unique[$i][1] $unique[$i][2]"; } print "\n";

Replies are listed 'Best First'.
Re: Removing duplicates in Array of Array
by GrandFather (Saint) on Sep 04, 2008 at 04:24 UTC

    Why the technology restriction?

    There is something of a disjunction between your problem description and the title of your node. What is it that you actually want to achieve? Maybe you should fill us in on some of the bigger picture?

    BTW, your sample could be better (for some definition of better) written as:

    use strict; use warnings; my @array = (["1", "1", "3"], ["1", "5", "6"], ["1", "1", "3"]); my @unique; my %seen; $seen{$_}++ or push @unique, $_ for @array; print "$_->[0] $_->[1] $_->[2]\n" for @unique;

    although @unique is only required if order is important.

    Oh, and your array initialization is completely broken. At the very least you should use the form I've shown above.


    Perl reduces RSI - it saves typing
      The sad story is I have a piece of code that can remove duplicates using grep:
      my @unique_terminal_id = grep { !($seen_values{$_->[4]}++) } @temp_ope +n_single; my %seen_values; my @unique_manufacturer_model = grep { !($seen_values{$_->[9]}{$_->[10 +]}++) } @unique_terminal_id; my %seen_values; my @unique_manufacturer_model_version = grep { !($seen_values{$_->[9]} +{$_->[10]}{$_->[11]}++) } @unique_terminal_id;
      But I discover that updating @unique_manufacturer_model also changes @unique_manufacturer_model_version. I was told that this is due to the fact that I am storing reference (cross-referencing trap). I was told to use Storeable(dclone), but I ca'nt figure out how to apply. So I was thinking if there could be a longer (inefficient) way of getting around this.

        Ah, so the bigger picture is that you need a deep copy of the elements from the original array? That is, at present you fall foul of:

        use strict; use warnings; my %seen; my @array = (["1", "1", "3"], ["1", "5", "6"], ["1", "1", "3"]); my @unique = grep {! $seen{$_->[1]}++} @array; print "@$_\n" for @array; print "\n"; map {$_++} @$_ for @unique; print "@$_\n" for @array; print "\n";

        Prints:

        1 1 3 1 5 6 1 1 3 2 2 4 2 6 7 1 1 3

        where you wanted @array unaffected by the manipulation of the contents of @unique. If you know that you are dealing with an AoA then you can:

        use strict; use warnings; my %seen; my @array = (["1", "1", "3"], ["1", "5", "6"], ["1", "1", "3"]); my @unique = map {[@$_]} grep {! $seen{$_->[1]}++} @array; print "@$_\n" for @array; print "\n"; map {$_++} @$_ for @unique; print "@$_\n" for @array; print "\n"; print "@$_\n" for @unique; print "\n";

        Prints:

        1 1 3 1 5 6 1 1 3 1 1 3 1 5 6 1 1 3 2 2 4 2 6 7

        Note the map used with the result from grep to copy the elements of each unique row? So actually rather than using neither grep nor map, what you really needed was to use both grep and map!


        Perl reduces RSI - it saves typing
Amazing syntax of push (was Re: Removing duplicates in Array of Array)
by Narveson (Chaplain) on Sep 04, 2008 at 04:49 UTC
    Oh, and your array initialization is completely broken.

    I thought (like GrandFather above) that there was something wrong with

    my @array; push @{@array},["1", "1", "3"]; push @{@array},["1", "5", "6"]; push @{@array},["1", "1", "3"];

    But your code runs fine, even using strict, and generates no warnings.

    I wondered if the @array inside the curlies was being evaluated in scalar context or as the literal '@array', but no. As far as I can tell with Data::Dumper, these statements populate @array.

    Never mind what you were trying to do, can any of our brethren explain what you've actually done?

      Seems you do not need either push or anonymous arrays to see the behaviour with 5.8.6:

      c:\test>perl -Mstrict -MData::Dump=pp -wle"my @r=1..10; @{ @r }='a'..'c'; pp \@r" ["a", "b", "c"]

      @{ @r } seems to function exactly the same as either @r alone (or @{ \@r }). Which is somewhat bizarre.

      But not so with 5.10

      c:\test>\perl510\bin\perl -MData::Dump=pp -wle"my @r=1..10; @{ @r }='a'..'c'; pp \@r" [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] c:\test>\perl510\bin\perl -Mstrict -MData::Dump=pp -wle"my @r=1..10; @{ @r }='a'..'c'; pp \@r" Can't use string ("10") as an ARRAY ref while "strict refs" in use at +-e line 1.

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        I see that I missed a step: run the program with perl 5.10.0. So, the program parses|compiles ok but causes run time error as above (in my example 10 in above error message is a 2). For the record, perl 5.8.8 produces neither compile time nor run time error.

      The seemingly obvious reasons that I can come up with are (a) either array in push @{ @p } , ... is magically being used as an array reference; (b) or the array deference is being optimized out. Given ...

      use warnings; use strict; my @p; my $p = \@p; push @p , [ 1 , 2 ]; push @{ $p } , [ 3 , 4 ]; push @{ @p } , [ 5 , 6 ];

      ... output of perl -MO=Terse file (5.8.8; in case somebody can make more sense of it than I can) ...

      perl 5.8.8 & 5.10.0 did not have any problems in parsing the code.

        rv2av doesn't seem to care whether it gets an array reference or an array.

        Update: This is confirmed by BrowserUK's less specific test and by looking at the 5.8.8 source:

        PP(pp_rv2av) { ... if (SvROK(sv)) { ... av = (AV*)SvRV(sv); ... } else { if (SvTYPE(sv) == SVt_PVAV) { av = (AV*)sv; ... } ... } ... ...return something derived from av... }

      When I run that code using Perl v5.8.7 I get the error:

      Bizarre copy of ARRAY in leave at C:\Delme~~\PerlScratch\noname.pl lin +e 2. Is this a version issue?

      without strictures.


      Perl reduces RSI - it saves typing