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

A coworker of mine was going over some sort of Perl quiz questions, and I was stumped by this one. The question wants you to remove duplicate values from an array without using a looping construct. This is the answer:
my @arr = (4, 1, 6, 4, 3, 7); my %saw; @saw{ @arr } = (); # The line I'm not understanding my @new_arr = keys %saw; print sort @new_arr;
Normally I would do this with map(), thusly:
my @arr = (4, 1, 6, 4, 3, 7); my %saw = map{ $_ => undef } @arr; my @new_arr = keys %saw; print sort @new_arr;

Which I think is much more clear, but I woud like to understand what the first version is doing. Can someone shed some light on this for me?

I'd do a search, but I'm not sure what to search for.

Replies are listed 'Best First'.
Re: De-Duping An Array
by ikegami (Patriarch) on Dec 21, 2006 at 21:09 UTC

    @saw{ @arr } = (); is a hash slice assignment. The following three snippets are functionally equivalent:

    @saw{ qw( a b c ) } = ( 1, 2, 3 );
    ( $saw{a}, $saw{b}, $saw{c} ) = ( 1, 2, 3 );
    $saw{a} = 1; $saw{b} = 2; $saw{c} = 3;

    Update: By the way, your solutions do not preserve the order of the items. If that was a requirement, you'd could use

    my @arr = (4, 1, 6, 4, 3, 7); my %saw; my @new_arr = grep !$saw{$_}++, @arr; print @new_arr;
      Ah, thanks very much. Preserving order wasn't a requirement, but that's cool to know.
Re: De-Duping An Array
by GrandFather (Saint) on Dec 21, 2006 at 21:12 UTC

    @saw{ @arr } is a hash slice. Take a look at the Slices section of perldata.


    DWIM is Perl's answer to Gödel
Re: De-Duping An Array
by shmem (Chancellor) on Dec 21, 2006 at 22:57 UTC
    @saw{ @arr } = (); # The line I'm not understanding

    Others said that this is a hash slice. Yeah, but how, and why?

    It's context again. Consider:

    {}; # hash reference []; # array reference %hash; # named hash $hash{'foo'} = 'bar'; # left-hand-side (LHS) of '=' in scala +r context @ary = (1,2,3); # LHS & RHS are in list context: # assign a list to an array named ' +ary' $hash{'foo'} = @ary; # LHS context(scalar) forces RHS scala +r context: # $hash{'foo'} == 3 , i.e. scalar(@ +ary) @hash{qw(foo bar quux)} = @ary; # LHS in list context, RHS in list con +text: # $hash{'bar'} == 2; $hash{'quux'} +== 3

    The prefixes ($,%,@,&) for identifiers enforce context. So, @hash{@ary} = @list uses the %hash in list context, for which use the (imho) hard to grasp idiom 'slice', for naming the whole LHS expression, has settled.

    --shmem

    update: replaced "an array slice" with "a hash slice". Of course I meant "hash slice", as per ikegami's comment below. Glitches the like often happen to me, whilst thinking ahead. But best was a friend who reported she once thought "seven", said "1" and wrote "4", all at the same time..

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      Others said that this is an array slice.

      Actually, they said it was a hash slice, and they were correct.

      Slices in perldata has good examples of slices used on the left hand side and on the right hand side of assignments.

Re: De-Duping An Array
by BrowserUk (Patriarch) on Dec 21, 2006 at 21:34 UTC

    To Anna Ray: De doop. De doop. De doop, de doop, de dooop. Da diddly doooooop. Da diddly doop!


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: De-Duping An Array
by ides (Deacon) on Dec 22, 2006 at 14:42 UTC

    While I realize this quiz probably mentioned not using CPAN modules, but here is what I would do in real code:

    use List::MoreUtils qw( uniq ); my @dupes = ( 4, 1, 6, 4, 3, 7); my @deduped = uniq( @dupes );

    Frank Wiles <frank@revsys.com>
    www.revsys.com