28

How do I get the total items in an array, not the last id?

None of two ways I found to do this works:

my @a;
# Add some elements (no consecutive ids)
$a[0]= '1';
$a[5]= '2';
$a[23]= '3';

print $#a, "\n"; # Prints 23
print scalar(@a), "\n"; # Prints 24

I expected to get 3...

0

6 Answers 6

40

Edit: Hash versus Array

As cincodenada correctly pointed out in the comment, ysth gave a better answer: I should have answered your question with another question: "Do you really want to use a Perl array? A hash may be more appropriate."

An array allocates memory for all possible indices up to the largest used so-far. In your example, you allocate 24 cells (but use only 3). By contrast, a hash only allocates space for those fields that are actually used.

Array solution: scalar grep

Here are two possible solutions (see below for explanation):

print scalar(grep {defined $_} @a), "\n";  # prints 3
print scalar(grep $_, @a), "\n";            # prints 3

Explanation: After adding $a[23], your array really contains 24 elements --- but most of them are undefined (which also evaluates as false). You can count the number of defined elements (as done in the first solution) or the number of true elements (second solution).

What is the difference? If you set $a[10]=0, then the first solution will count it, but the second solution won't (because 0 is false but defined). If you set $a[3]=undef, none of the solutions will count it.

Hash solution (by yst)

As suggested by another solution, you can work with a hash and avoid all the problems:

$a{0}  = 1;
$a{5}  = 2;
$a{23} = 3;
print scalar(keys %a), "\n";  # prints 3

This solution counts zeros and undef values.

Sign up to request clarification or add additional context in comments.

5 Comments

The last part of this answer is the correct one. grillix seems to be from a PHP background. What PHP calls "arrays" are actually more akin to Perl's hashes, and the latter should be used in cases like this.
How can this idea be extended to multi-dimensional arrays in perl?
Small fix to the grep format: print scalar(grep { $_ } @a), "\n";
@arikin: I added a comma to fix the syntax problem. Thanks for pointing it out!
You can safely omit keys call in the hash solution: simple scalar %a is sufficient
16

It sounds like you want a sparse array. A normal array would have 24 items in it, but a sparse array would have 3. In Perl we emulate sparse arrays with hashes:

#!/usr/bin/perl

use strict;
use warnings;

my %sparse;

@sparse{0, 5, 23} = (1 .. 3);

print "there are ", scalar keys %sparse, " items in the sparse array\n",
    map { "\t$sparse{$_}\n" } sort { $a <=> $b } keys %sparse;

The keys function in scalar context will return the number of items in the sparse array. The only downside to using a hash to emulate a sparse array is that you must sort the keys before iterating over them if their order is important.

You must also remember to use the delete function to remove items from the sparse array (just setting their value to undef is not enough).

3 Comments

This is right on. However, Tie::IxHash is optional; in your example it seems unnecessary. Also, no need to provide the predicate to sort, as that is the default. "sort keys %sparse" works as well.
Whoops, the Tie::IxHash is left over from a different example. Let me remove that.
@Spoulson No the default sort is lexical not numeric, so keys (1, 2, and 10) will be sorted (1, 10, 2).
14

Maybe you want a hash instead (or in addition). Arrays are an ordered set of elements; if you create $foo[23], you implicitly create $foo[0] through $foo[22].

Comments

8
print scalar grep { defined $_ } @a;

4 Comments

Explanation: perl does not really have "sparse" arrays as grilix wants them. If you say "my @a; $a[10]=5;" then perl creates an array with 11 entries: the first 10 are filled with 'undef', the 11th is filled with '5'. What "scalar @a" and "$#a" report is always the overall length/last index. kcwu filters the array to count only the defined entries.
It works, but it is not good. The grep function is O(n), which means if you have @a[1, 1_000_000] = (1, 2); then it has to look at each of the 1,000,000 items to get you a count, it also means that you will be taking up a bunch of memory for no reason, use a hash instead.
Ya.. It works IF I HAVE TO use arrays, but I think I can use hashes instead. Anyway, he just response what I've asked for. Thank you all.
Can you add an explanation to your answer? But without "Edit:", "Update:", or similar - the answer should appear as if it was written today.
1
@people = qw( bob john linda ); 
$n = @people; # The number 3
Print " The number in the list is $n \n"; 

Expressions in Perl always return the appropriate value for their context.

For example, how about the “name” * of an array? In a list context, it gives the list of elements. But in a scalar context, it returns the number of elements in the array.

Comments

0
sub uniq {
    return keys %{{ map { $_ => 1 } @_ }};
}
my @my_array = ("a","a","b","b","c");
#print join(" ", @my_array), "\n";
my $a = join(" ", uniq(@my_array));
my @b = split(/ /,$a);
my $count = $#b;

1 Comment

This code has serious problems. First are foremost, it is broken if the items in the array contain spaces. Secondly, it iterates over the full set of array items once and the set of defined items twice. Thirdly, like all of the array based solutions, it has no way of distinguishing undefs the user set vs empty slots in the array (this one may not be bad depending on how the code is used).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.