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

I want to take a list of inputs and simply print out each element in the order in which they occur.

This sounds simple, but my code doesn't work (prints a blank line).

#!/usr/bin/perl -w use strict; my @inputs = (); my @values = (); @inputs = @ARGV; foreach my $elemnt (@inputs) { if (grep {$_ ne $elemnt} @values) { push @values, $elemnt; } } print "@values\n";

Suspecting an initialization problem, I tried an alternate version where I pre-seeded one element in @values, but that printed every input value (not removing duplicates).

Replies are listed 'Best First'.
Re: Finding unique elements in inputs
by GrandFather (Saint) on Apr 21, 2023 at 04:32 UTC

    grep can only return anything if it has something to chose from. @value starts out empty so grep can never return anything except an empty list so the if expression can never be true so @value never gets any content.

    The usual way to check if something has been seen before is to use a hash:

    #!/usr/bin/perl -w use strict; my @inputs = qw(first second third fourth fifth first); my %found; my @values; for my $element (@inputs) { if (!$found{$element}) { push @values, $element; ++$found{$element}; } } print "@values\n";

    Prints:

    first second third fourth fifth
    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: Finding unique elements in inputs
by haukex (Archbishop) on Apr 21, 2023 at 04:42 UTC
    grep {$_ ne $elemnt} @value

    In this context, this means "count how many elements in @values don't equal $element". When @values is initially empty, this will always be zero and therefore false. See the Basic debugging checklist, adding prints of things like @values and the condition in the loop will be helpful, especially with Data::Dumper. I suspect you meant not grep {$_ eq $elemnt} @values instead, however, this will be inefficient for larger arrays, and as GrandFather already said, the typical solution is to use a hash. See also How can I remove duplicate elements from a list or array?

Re: Finding unique elements in inputs (uniq)
by 1nickt (Canon) on Apr 21, 2023 at 12:11 UTC

    use strict; use warnings; use List::Util 'uniq'; use Test::More; my @input = (1,2,2,3,4,5,2,1); my @wanted = (1,2,3,4,5); my @got = uniq @input; is_deeply(\@got, \@wanted); done_testing; __END__

    Hope this helps!


    The way forward always starts with a minimal test.
Re: Finding unique elements in inputs
by jwkrahn (Abbot) on Apr 21, 2023 at 08:58 UTC
    $ perl -le' my @values = do { my %unique; grep ! $unique{ $_ }++, @ARGV; }; print "@values"; ' one two three four one five two one two three four five
    Naked blocks are fun! -- Randal L. Schwartz, Perl hacker
Re: Finding unique elements in inputs
by BillKSmith (Monsignor) on Apr 22, 2023 at 15:22 UTC
    Although others have shown better solutions, The original post is nearly correct. The only problem is that the logic is backwards.
    #!/usr/bin/perl -w use strict; @ARGV = qw(a b c c d); #remove the extra "c" my @inputs = (); my @values = (); @inputs = @ARGV; foreach my $elemnt (@inputs) { # if (grep {$_ ne $elemnt} @values) if (!grep {$_ eq $elemnt} @values) { push @values, $elemnt; } } print "@values\n";
    Bill
Re: Finding unique elements in inputs
by kcott (Archbishop) on Apr 23, 2023 at 02:55 UTC

    I've been watching this thread for the last day or so. The problem with your code has been identified. You've received a number of working solutions, all of which I've upvoted.

    Having said that, all solutions here seem more complicated than is necessary. To my mind, the following is really all you need:

    $ perl -e ' my %seen; my @values = grep !$seen{$_}++, @ARGV; print "@values\n"; ' 1 2 1 2 3 1 2 3 1 2 3

    There are a number of ways this can be shortened. Here are a couple of examples:

    $ perl -E ' local $, = " "; say grep {state $seen; !$seen->{$_}++} @ARGV; ' 1 2 1 2 3 1 2 3 1 2 3
    $ perl -E 'say "@{[grep {state $seen; !$seen->{$_}++} @ARGV]}";' 1 2 1 + 2 3 1 2 3 1 2 3

    However, there are potential drawbacks to these:

    • They involve cleverness. In other words, you, or other maintainers, may not immediately understand the code on subsequent inspection. This can reduce maintainability — even if that just involves the time needed for research. For instance, at first glance, without referring to doco, do you know what these mean:
    • You'll need Perl v5.10 or later.
    • If this code exists in a loop with many iterations, or an often called subroutine, you may start to see efficiency degradation. grep has two forms, grep BLOCK LIST & grep EXPR,LIST. The BLOCK form is slower. Use Benchmark if this is important in your code.

    — Ken