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

I am trying to figure out the best way to test for the number of elements in an array. Basically, if the array only has one element, I want to do A, and if it has anything else (meaning 0 or > 1) I want it to do B. I am currently doing "if (@foo == 1) ...", but not sure if that really works... I have code below for the different ways I know to do this... Please give me any suggestions. Maybe there is a better way?
# foo only has one element... if (@foo == 1) { ... }
my $num_elements=@foo; if ($num_elements == 1) { ... }
if ($foo[0] ne "" && $foo[1] eq "") { ... }

Replies are listed 'Best First'.
Re: Test Number of Elements In Array
by jethro (Monsignor) on Jun 05, 2009 at 17:03 UTC

    No, your first method is the best and most readable way. == imposes scalar context so this is safe as well.

    If you are looking for other ways, if (not (@foo-1)) or if (not $#foo) achieve the same, but are not readable anymore.

Re: Test Number of Elements In Array
by toolic (Bishop) on Jun 05, 2009 at 17:17 UTC
    but not sure if that really works...
    Then just test it for yourself:
    use strict; use warnings; my @a0; my @a1 = 1; my @a2 = 0..5; check(@a0); check(@a1); check(@a2); sub check { if (@_ == 1) { #if ((scalar @_) == 1) { print "exactly 1\n"; } else { print "diff from 1\n"; } } __END__ diff from 1 exactly 1 diff from 1

    Your original method seems to work pretty well.

    You could make it more verbose by using scalar. You decide which is simpler for you to understand.

      You could make it more verbose by using scalar.
      More verbose, but absolutely unambiguous to the future reader, who may even be the original author.

        If a future reader does not understand scalar context in Perl (especially in this hoary idiom for counting the number of elements in an array!), you have far greater worries than whether he or she will understand this expression.

Re: Test Number of Elements In Array
by liverpole (Monsignor) on Jun 06, 2009 at 16:13 UTC
    Hi walkingcow,

    Adding my 2 cents here ...

    A trick I like for comparisons against a constant (for both arrays and scalars), is to put the constant on the left side of the == operator.

    This has the nice effect of making it illegal to accidentally drop one of the '=' signs.

    For example, in:

    use strict; use warnings; my @array = ( 1, 2, 3 ); print "@array\n"; # Time passes... if (@array = 10) { # A typo -- should have been "(@array == 10)" print "@array\n"; # Causes an assignment, and a TRUE evaluation +! } else { print "The array doesn't have 10 values\n"; } # Prints: # 1 2 3 # 10 @array = ( 1, 2, 3 ); print "@array\n"; # Time passes... if (10 == @array) { # Can't accidentally do "(10 = @array)" print "@array\n"; # without getting a fatal error. } else { print "The array doesn't have 10 values\n"; } # Prints: # 1 2 3 # The array doesn't have 10 values

    If you were to accidentally change "==" to "=" in "if (10 == @array" (or someone in the future did the same), the resulting error would reveal it:

    Can't modify constant item in scalar assignment at x.pl line 26, near "@array) " Execution of x.pl aborted due to compilation errors.

    I use it all the time now by habit, and I never have to worry about accidentally assigning instead of comparing. Of course, ymmv...


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
      I use it all the time now by habit, and I never have to worry about accidentally assigning instead of comparing.
      The downsite is having "magic" numbers in your source code. And the trick only works when comparing against a literal, not a variable. If you write:
      my $SPECIAL_SIZE = 10; ... if ($SPECIAL_SIZE == @array) { ... }
      and you mistype '=' instead of '==', you're out of luck again. Now, using use constant SPECIAL_SIZE => 10; will save you, but constants defined that way aren't easy to interpolate.

      As usual with programming, doing X to avoid Y compromises on Z. Personally, I prefer not having 'magic numbers' in my source code, relying on test cases to avoid the '=' typo.

                "The downsite is having "magic" numbers in your source code."

        One doesn't introduce any new "magic numbers", if instead of writing:

        if (@wishes == 3) { print "The genie has granted all of your wishes\n"; }

        One writes:

        if (3 == @wishes) { print "The genie has granted all of your wishes\n"; }

                "And the trick only works when comparing against a literal, not a variable."

        Granted.  But that doesn't make it any less useful to use for constants, which was my point.

        Speaking of magic numbers, I think one can get too carried away with their usage.  I would argue the following goes a bit overboard ...

        my $days_per_year = 365; # In case the number of days in a year chang +es ;-) ... if (@calendar < $days_per_year) { warn "Hold on, Cinderella -- you're calendar isn't full yet.\n"; }

        The above example is based on a college classmate of mine who, back in the days of BASIC, had the constant "dpy" ("Days per year") in a startup script, so when he needed it, he could type "dpy" instead of "365"!


        s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: Test Number of Elements In Array
by ig (Vicar) on Jun 08, 2009 at 06:04 UTC

    While @array is suitable in many cases, you should be mindful of the possibility of sparsely populated arrays. Consider the following and what numbers might be considered to be "the number of elements in the array".

    use strict; use warnings; use Devel::Peek; my @array; $array[5] = 'data'; print "\@array = " . scalar(@array) . "\n"; Dump(\@array, 10); __END__ @array = 6 SV = RV(0x913534c) at 0x9135340 REFCNT = 1 FLAGS = (TEMP,ROK) RV = 0x9145430 SV = PVAV(0x9136250) at 0x9145430 REFCNT = 2 FLAGS = (PADMY) ARRAY = 0x91522e0 FILL = 5 MAX = 5 ARYLEN = 0x0 FLAGS = (REAL) Elt No. 0 Elt No. 1 Elt No. 2 Elt No. 3 Elt No. 4 Elt No. 5 SV = PV(0x9133048) at 0x91351f0 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x9140928 "data"\0 CUR = 4 LEN = 8