Counting the total number of keys in a hash of hashes

  • Thread starter Mahurshi Akilla
  • Start date
M

Mahurshi Akilla

Is there an easy way to count the # of keys in a hash of hashes?

e.g.
Say I have a hash like the following:
$myhash{key1}{key2}

I want to count the total number of keys in $myhash without looping
through the whole thing. The answer is typically #key1 x #key2

Taking this one step further, I would like to know if there's a way to
do the same for "higher depths" like
$myhash{key1}{key2}{key3} and so on...

It will be really cool if there is a built in function/perl module
that can do this.
 
S

sln

Is there an easy way to count the # of keys in a hash of hashes?

e.g.
Say I have a hash like the following:
$myhash{key1}{key2}

I want to count the total number of keys in $myhash without looping
through the whole thing. The answer is typically #key1 x #key2

Whats the reason you want to count all the keys in a hash?
Well there's keys %myhash.
"#key1 x #key2" ?? No, thats not right, its a hash.
Taking this one step further, I would like to know if there's a way to
do the same for "higher depths" like
$myhash{key1}{key2}{key3} and so on...

Deep recursion, test for hash ref's, use keys %{}
It will be really cool if there is a built in function/perl module
that can do this.

I'm sure there is a module out there. But, name one 'cool' reason to
get a count of keys?


untested, something like this (unblessed):


sub get_key_count
{
my ($href,$count) = @_;
foreach my $key (keys %{$href}) {
$$count++;
if ( ref ($href->{$key}) eq 'HASH' ) {
get_key_count ($href->{$key}, $count);
}
}
}


-sln
 
S

sln

Whats the reason you want to count all the keys in a hash?
Well there's keys %myhash.
"#key1 x #key2" ?? No, thats not right, its a hash.


Deep recursion, test for hash ref's, use keys %{}


I'm sure there is a module out there. But, name one 'cool' reason to
get a count of keys?


untested, something like this (unblessed):


sub get_key_count
{
my ($href,$count) = @_;
foreach my $key (keys %{$href}) {
$$count++;
if ( ref ($href->{$key}) eq 'HASH' ) {
get_key_count ($href->{$key}, $count);
}
}
}

But, I guess you would have to check all the elements if ARRAY for
hash ref's as well.

if ( ref ($href->{$key}) eq 'ARRAY' ) {
# loop through the array to find HASH refs
# call get_key_count (..., $count);
}

-sln
 
J

Jürgen Exner

Mahurshi Akilla said:
Is there an easy way to count the # of keys in a hash of hashes?

e.g.
Say I have a hash like the following:
$myhash{key1}{key2}

I want to count the total number of keys in $myhash without looping
through the whole thing.

There is no way to recursively count the keys without looking at each
hash individually at some point. You can either do it manually (for() or
map()) or have some other tool do it for you (Data::Dumper comes to
mind) and then analyse the output of that tool. But you have to loop
through the whole structure at some point.
The answer is typically #key1 x #key2

Why would you think so? That's a very special case.
Typically %{$myhash{foo}} and %{$myhash{bar}} will have very different
members and different lengths.
If you data structure happens to be that regular by chance then you
already got your answer.
Taking this one step further, I would like to know if there's a way to
do the same for "higher depths" like
$myhash{key1}{key2}{key3} and so on...
It will be really cool if there is a built in function/perl module
that can do this.

It's a two-liner, too trivial to put in a module. And rarely needed, I
would guess. At least I can't think of a good reason why I would want to
compute the total sum of unrelated items.

jue
 
D

Dr.Ruud

Mahurshi said:
Is there an easy way to count the # of keys in a hash of hashes?

e.g.
Say I have a hash like the following:
$myhash{key1}{key2}

I want to count the total number of keys in $myhash without looping
through the whole thing. The answer is typically #key1 x #key2

Taking this one step further, I would like to know if there's a way to
do the same for "higher depths" like
$myhash{key1}{key2}{key3} and so on...

It will be really cool if there is a built in function/perl module
that can do this.

What problem are you trying to solve?
 
S

sln

Is there an easy way to count the # of keys in a hash of hashes?

e.g.
Say I have a hash like the following:
$myhash{key1}{key2}

I want to count the total number of keys in $myhash without looping
through the whole thing. The answer is typically #key1 x #key2

Taking this one step further, I would like to know if there's a way to
do the same for "higher depths" like
$myhash{key1}{key2}{key3} and so on...

It will be really cool if there is a built in function/perl module
that can do this.

I think you can get the general metrics of variables in a fashion
like below. This is a form of 'template' how deep recursion works.
As you can see, it takes considerably more than a 2-liner that someone
suggested in this thread. Otherwise Dumper wouldn't be appretiated.

Deep recursion is complex to say the least. It has many uses,
especially with regard to cleaning (freeing) variables.

-sln

-------------------------
use strict;
use warnings;
use Data::Dumper;

my @ar1 = ('autoTestSoftware001',{50 => 'autoTestSoftware050'});
my @ar2 = ('autoTestSoftware051',{100 => 'autoTestSoftware100'}, ar1 => \@ar1);

#my $softwareListRef = [\(@ar1, @ar2)];
my $softwareListRef = {root => [\(@ar1, @ar2)]};

print Dumper( $softwareListRef );

my $result = getVariableMetrics ($softwareListRef);

print <<MSG;
Metrics:
Hashs = $result->{hashs}
Keys = $result->{keys}
Arrays = $result->{arrays}
Others = $result->{others}
MSG

for (keys %{$result->{hseen}}) {
print " Seen = $_\n";
}

sub getVariableMetrics # '$var' can be anything
{
my ($var, $result) = @_;
$result = {
hashs => 0,
keys => 0,
arrays => 0,
others => 0,
hseen => {}
} if (!defined $result);

if ( ref ($var) eq 'HASH') { # Hashs
return $result if ( exists $$result{hseen}{$var});
$result->{hashs}++;
$$result{hseen}{$var} = 1;
while (my ($key, $val) = each %{$var}) { # Keys
$result->{keys}++;
if ( ref ($val) eq 'HASH' || ref ($val) eq 'ARRAY' ) {
getVariableMetrics ($val, $result);
}
}
}
elsif ( ref ($var) eq 'ARRAY') { # Arrays
return $result if ( exists $$result{hseen}{$var});
$result->{arrays}++;
$$result{hseen}{$var} = 1;
for (@$var) {
if ( ref ($_) eq 'HASH' || ref ($_) eq 'ARRAY' ) {
getVariableMetrics ($_, $result);
} else {
$result->{others}++;
}
}
} else { # Others (add more here)
$result->{others}++;
}
return $result;
}
__END__

Output:

$VAR1 = {
'root' => [
[
'autoTestSoftware001',
{
'50' => 'autoTestSoftware050'
}
],
[
'autoTestSoftware051',
{
'100' => 'autoTestSoftware100'
},
'ar1',
$VAR1->{'root'}[0]
]
]
};
Metrics:
Hashs = 3
Keys = 3
Arrays = 3
Others = 3
Seen = ARRAY(0x18b44b4)
Seen = HASH(0x18c4e2c)
Seen = HASH(0x22ab94)
Seen = ARRAY(0x182abbc)
Seen = HASH(0x22ac84)
Seen = ARRAY(0x182abac)
 
S

sln

Is there an easy way to count the # of keys in a hash of hashes?

e.g.
Say I have a hash like the following:
$myhash{key1}{key2}

I want to count the total number of keys in $myhash without looping
through the whole thing. The answer is typically #key1 x #key2

Taking this one step further, I would like to know if there's a way to
do the same for "higher depths" like
$myhash{key1}{key2}{key3} and so on...

It will be really cool if there is a built in function/perl module
that can do this.

I shouldn't post code like this so I won't any more. I guess it goes
into my archives as a backup for future reference. I was mainly interrested
in deep recursion.

Anyway, refactored here. The thrust was on the metrics of a deep structure,
but I added the hooks to do deep array/hash clearing. It is mainly an
exercise for me but if the metric code is taken out, its actually not much
to the recursion. Obviously, anything could be added to the recursion frame
to do all sorts of things.

Tested as well.

-sln

use strict;
use warnings;
use Data::Dumper;

my @ar1 = ('autoTestSoftware001',{50 => 'autoTestSoftware050'});
my @ar2 = ('autoTestSoftware051',{100 => 'autoTestSoftware100'});

push @ar1, [ar1 => \@ar2];

#my $softwareListRef = [\(@ar1, @ar2)];
my $softwareListRef = {root => [\(@ar1, @ar2)]};

push @ar1, {SWLF => $softwareListRef};

print Dumper( $softwareListRef );

my $result = getVariableMetrics ($softwareListRef, 'purge' => 1);

print <<MSG;
Metrics:
Hashs = $result->{hashs}
Keys = $result->{keys}
Arrays = $result->{arrays}
Others = $result->{others}
Depth = $result->{depth}
MSG

for (keys %{$result->{hseen}}) {
print " Seen = $result->{hseen}{$_} x $_\n";
}

print Dumper( \@ar1 );
print Dumper( \@ar2 );
print Dumper( $softwareListRef );

exit (0);

## -------------

sub getVariableMetrics # '$var' can be anything
{
my ($var, @args) = @_;
$result =
{ # Scalar Results and misc vars:
hashs => 0, # total hashes
keys => 0, # total keys
arrays => 0, # total arrays
others => 0, # total others
level => 0, # current level
depth => 0, # max depth
purge => 0, # flag to purge
hseen => {} # stringed array/hash refs seen
};
if (@args) {
while (my ($name, $val) = splice (@args, 0, 2)) {
$name =~ s/^\s+//; $name =~ s/\s+$//;
if (lc $name eq 'purge') {
$result->{purge} = 1 if $val;
}
}
}
_GVM ($var, $result);
}

sub _GVM
{
my ($var, $result) = @_;

# Hash ref's
if ( ref ($var) eq 'HASH') {
return $result if ( ++$result->{hseen}{$var} > 1);
if ( ++$result->{level} > $result->{depth}) {
$result->{depth} = $result->{level}
}
$result->{hashs}++;
for (values %{$var}) {
# go deeper into the key value
$result->{keys}++;
_GVM ($_, $result);
}
$result->{level}--;

# deepest level here, hash can be deleted
%{$var} = () if ($result->{purge});
}
# Array ref's
elsif ( ref ($var) eq 'ARRAY') {
return $result if ( ++$result->{hseen}{$var} > 1);
if ( ++$result->{level} > $result->{depth}) {
$result->{depth} = $result->{level}
}
$result->{arrays}++;
for (@{$var}) {
# go deeper into array element
_GVM ($_, $result);
}
$result->{level}--;

# deepest level here, array can be deleted
@{$var} = () if ($result->{purge});
}

# Other scalars (add more processing here)
else {
$result->{others}++;
}
return $result;
}
__END__

$VAR1 = {
'root' => [
[
'autoTestSoftware001',
{
'50' => 'autoTestSoftware050'
},
[
'ar1',
[
'autoTestSoftware051',
{
'100' => 'autoTestSoftware100'
}
]
],
{
'SWLF' => $VAR1
}
],
$VAR1->{'root'}[0][2][1]
]
};
Metrics:
Hashs = 4
Keys = 4
Arrays = 4
Others = 5
Depth = 6
Seen = 1 x ARRAY(0x18c4abc)
Seen = 2 x HASH(0x18c529c)
Seen = 1 x HASH(0x22ab94)
Seen = 1 x ARRAY(0x182abbc)
Seen = 1 x HASH(0x22ac84)
Seen = 2 x ARRAY(0x182abac)
Seen = 1 x ARRAY(0x182b3dc)
Seen = 1 x HASH(0x22aa74)
$VAR1 = [];
$VAR1 = [];
$VAR1 = {};
 
S

sln

Is there an easy way to count the # of keys in a hash of hashes?

e.g.
Say I have a hash like the following:
$myhash{key1}{key2}

I want to count the total number of keys in $myhash without looping
through the whole thing. The answer is typically #key1 x #key2
[snip]

use strict;
use warnings;
use Data::Dumper;

my @ar1 = ('autoTestSoftware001',{50 => 'autoTestSoftware050'});
my @ar2 = ('autoTestSoftware051',{100 => 'autoTestSoftware100'});

push @ar1, [ar1 => \@ar2];

#my $softwareListRef = [\(@ar1, @ar2)];
my $softwareListRef = {root => [\(@ar1, @ar2)]};

push @ar1, {SWLF => $softwareListRef};

print Dumper( $softwareListRef );

my $result = getVariableMetrics ($softwareListRef, 'purge' => 1);

print <<MSG;
Metrics:
Hashs = $result->{hashs}
Keys = $result->{keys}
Arrays = $result->{arrays}
Others = $result->{others}
Depth = $result->{depth}
MSG

for (keys %{$result->{hseen}}) {
print " Seen = $result->{hseen}{$_} x $_\n";
}

print Dumper( \@ar1 );
print Dumper( \@ar2 );
print Dumper( $softwareListRef );

exit (0);

## -------------

sub getVariableMetrics # '$var' can be anything
{
my ($var, @args) = @_;
$result =
^^^^^^^
will run afoul here if not a function scoped my variable
{ # Scalar Results and misc vars:

Obviously a cut n' paste error when refactored.

I would visually distinquish variable names:

-------------------
[chop]
my $metric = getVariableMetrics ($softwareListRef, 'purge' => 1);

print <<MSG;
Metrics:
Hashs = $metric->{hashs}
Keys = $metric->{keys}
Arrays = $metric->{arrays}
Others = $metric->{others}
Depth = $metric->{depth}
MSG

for (keys %{$metric->{hseen}}) {
print " Seen = $metric->{hseen}{$_} x $_\n";
}
[chop]
## -------------

sub getVariableMetrics # '$var' can be a ref to anything
{
my ($var, @args) = @_;
my $result =
{ # Scalar Results and misc vars:
[chop]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,756
Messages
2,569,533
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top