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

I would like to get the total number of characters in a hash, keys plus values. For instance:
{ 'key' => 'value' } #should be 7
The only thing I can find that does anything close to this is Devel::Size, but it gives the total amount of memory used by the hash, which is interesting, but not what I want:
use strict; use warnings; use Devel::Size('total_size'); use feature 'say'; my $testme = { 'key' => 'value' }; say total_size($testme); # Output is "223"
Is there a package that will do what I want?

Replies are listed 'Best First'.
Re: Get total number of characters in a hash (not memory usage)
by haukex (Archbishop) on Jan 28, 2020 at 20:54 UTC
    { 'key' => 'value' } # should be 7

    Should be 8, shouldn't it? Anyway, you can take advantage of the fact that a hash in list context evaluates to the key/value pairs.

    my $hashref = { 'key' => 'value' }; my $sum1 = 0; $sum1 += length for %$hashref; # - or - my $sum2 = length join '', %$hashref; # - or - use List::Util qw/sum/; my $sum3 = sum map {length} %$hashref;

    I suspect the first of these will be most efficient since it doesn't build an intermediate string or list - Update: at least, I think it doesn't; I hope for %hash is optimized to use an iterator, but I'm not certain at the moment. If you wanted to play it super safe:

    my $sum4 = 0; while ( my ($k, $v) = each %$hashref ) { $sum4 += length($k) + length($v) }
Re: Get total number of characters in a hash (not memory usage)
by davido (Cardinal) on Jan 28, 2020 at 21:43 UTC

    use List::Util 'reduce'; sub hash_char_count :prototype(\%) { my $href = shift; return reduce {$a += length $b} 0, %$href; } my %hash = (foo => 'bar', baz => 1, fiddle => 'faddle'); print hash_char_count(%hash), "\n";

    I like List::Util::reduce for this. It's almost too trivial to bother with a subroutine around it, except that you get to give subroutines descriptive names so people don't have to puzzle over what it's doing.

    Sorry for the use of a prototype. Here's without:

    # ... sub hash_char_count { # ... } # ... print hash_char_count(\%hash), "\n";

    Dave

Re: Get total number of characters in a hash (not memory usage)
by choroba (Cardinal) on Jan 28, 2020 at 21:38 UTC
    > should be 7

    Really? Like this?

    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; my $hash = { 'key' => 'value' }; my %chars; $chars{$_} ++ for split //, join "", %$hash; my $count = keys %chars; say $count;
    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Get total number of characters in a hash (not memory usage)
by gurung (Sexton) on Jan 29, 2020 at 11:41 UTC
    perl -le '$h={"key"=>"value"}; $sum += length foreach (keys %$h, values %$h); print $sum'
Re: Get total number of characters in a hash (not memory usage)
by TieUpYourCamel (Scribe) on Jan 29, 2020 at 15:44 UTC
    Thanks for your suggestions everyone. I suppose my SSCCE was a little too simplified. What I'm evaluating now is actually an array of hashes, for which a few modifications to Davido's soluation seems to work best:
    use strict; use warnings; use List::Util 'reduce'; sub hash_char_count { my $orders = shift; my $count = 0; foreach my $order (@$orders) { $count += reduce { $a += length $b } 0, %$order; } return $count; } my $orders = [ { 'OrderDate' => '2019-11-01 00:00:00', 'AlternateKey' => 'D', 'Customer' => '1238756', 'SalesOrder' => '10928', 'CustomerName' => 'Dunder Mifflin', 'ShippingInstrs' => 'Standard' }, { 'OrderDate' => '2020-01-15 00:00:00', 'SalesOrder' => '11349', 'Customer' => '1239451', 'AlternateKey' => 'R', 'CustomerName' => 'Vance Refrigeration', 'ShippingInstrs' => 'Standard' }, { 'OrderDate' => '2020-01-22 00:00:00', 'SalesOrder' => '12954', 'Customer' => '1240029', 'AlternateKey' => 'R', 'CustomerName' => 'A1A Car Wash', 'ShippingInstrs' => 'Next Day' } ]; print hash_char_count($orders);
    I'd like to be able to get the character count of any data structure: array of hashes, array of hashes with some array elements, etc... but this works for me for now.

      Handles any nesting of [] and {}

      #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11111985 use warnings; use List::Util qw( sum0 ); my $orders = [ { 'OrderDate' => '2019-11-01 00:00:00', 'AlternateKey' => 'D', 'Customer' => '1238756', 'SalesOrder' => '10928', 'CustomerName' => 'Dunder Mifflin', 'ShippingInstrs' => 'Standard' }, { 'OrderDate' => '2020-01-15 00:00:00', 'SalesOrder' => '11349', 'Customer' => '1239451', 'AlternateKey' => 'R', 'CustomerName' => 'Vance Refrigeration', 'ShippingInstrs' => 'Standard' }, { 'OrderDate' => '2020-01-22 00:00:00', 'SalesOrder' => '12954', 'Customer' => '1240029', 'AlternateKey' => 'R', 'CustomerName' => 'A1A Car Wash', 'ShippingInstrs' => 'Next Day' } ]; sub charcount { my $tree = shift; ref $tree or return length $tree; sum0 map charcount($_), ref $tree eq 'HASH' ? %$tree : @$tree; } print charcount($orders), "\n";