简体   繁体   中英

How do I update values in an array of hashes, which is in a hash of a hash in perl?

Seems very confusing I know. I'll try to "draw" this data structure:

hash-> key->( (key)->[(key,value),(key,value),(key,value),...], (key,value))

So there is the first key, whose value is enclosed in the parenthesis. The value for the first key of the hash is two keys, one (the right one) being another simple key, value pair. The other (the left one) key's value is an array of hashes. I am able to update the "right" key, value pair with the following line of code:

$hash{$parts[1]}{"PAGES"} += $parts[2];

Where $parts[1] and $parts[2] are just elements from an array. I am +=ing a number to the "right" key, value pair from my hash. What I need to do now is update the "left" key,value pair - the array of hashes within a hash of hashes. Here is how I initialize the array for both key, value pairs in the hash of hashes:

$hash{$printer}{"PAGES"} = 0;
$hash{$printer}{"USERS"} = [@tmp];

Here is one of my many attempts to access and update the values in the array of hashes:

$hash{$parts[1]}{"USERS"}[$parts[0]] += $parts[2];

I just can't figure out the correct syntax for this. If anyone could help me I'd appreciate it. Thanks.

Edit: I guess a more pointed question is: How do I get a hash key from an array of hashes (keeping in mind that the array is in a hash of hashes)?

Edit: Added this to the code:

#Go through each user to check to see which user did a print job and add the page
#count to their total
#$parts[0] is the user name, $parts[1] is the printer name, $parts[2] is the page
#count for the current print job
for(my $i=0;$i<$arr_size;$i++)
{
    my $inner = $hash{$parts[1]}{"USERS"}[$i];
    my @hash_arr = keys %$inner;
    my $key = $hash_arr[0];

    #problem line - need to compare the actual key with $parts[0]
    #(not the key's value which is a number)
    if($hash{$parts[1]}{"USERS"}[$i]{$key} eq $parts[0])
    {
        $hash{$parts[1]}{"USERS"}[$i]{$parts[0]} += $parts[2];
    }   
}

Edit: Whoops hehe this is what I needed. It still isn't quite there but this is kind of what I am looking for:

if($key eq $parts[0])
{
    $hash{$parts[1]}{"USERS"}[$i]{$parts[0]} += $parts[2];
}

Edited to respond to the edited question: How do I get a hash key from an array of hashes (keeping in mind that the array is in a hash of hashes).

use strict;
use warnings;

my %h;
$h{printer}{PAGES} = 0;
$h{printer}{USERS} = [
    {a => 1, b => 2},
    {c => 3, d => 4},
    {e => 5, f => 6},
];

# Access a particular element.
$h{printer}{USERS}[0]{a} += 100;

# Access one of the inner hashes.
my $inner = $h{printer}{USERS}[1];
$inner->{$_} += 1000 for keys %$inner;

# Ditto, but without the convenience variable.
$h{printer}{USERS}[2]{$_} += 9000 for keys %{ $h{printer}{USERS}[2] };

use Data::Dumper qw(Dumper);
print Dumper \%h;

Output:

$VAR1 = {
     'printer' => {
             'PAGES' => 0,
             'USERS' => [
                   {
                    'a' => 101,
                    'b' => 2
                   },
                   {
                    'c' => 1003,
                    'd' => 1004
                   },
                   {
                    'e' => 9005,
                    'f' => 9006
                   }
                  ]
            }
    };

I have trouble understanding the structure from your description.

My advice is to avoid such structures like pestilentious devils from bad neighbourhoods.

One reason people end up with such maintenance nightmares is they're using XML::Simple .

Whatever the reason in your example, do yourself a favour and prevent that horrible data structure from ever getting created. There are always alternatives. If you describe the problem, people will be able to suggest some.

The way you've described the structure is somewhat impenetrable to me, but accessing array references embedded within other structures is done thusly, using a simpler example structure:

my $ref = {k => [1, 3, 5, 6, 9]};

Below, the value of 6 is incremented to 7:

$ref->{k}->[3] += 1;

See perlref for more details on the -> operator, but briefly, the expression to the left of the arrow can be anything that returns a reference. In some cases, the -> operator is optional but it's best left in for clarity.

I still haven't decoded your structure. But, I'll make two comments:

  1. Use the -> syntactic sugar. For example, $hash->{key}->[2]->{key} is a bit clearer than trying to parse things out without the syntactic sugar: ${{hash}{key}}[1]{key} (if that's even correct...)

  2. Look into using Object Oriented Perl for this structure. It's not as scary as it sounds. In Perl, objects are subroutines that handle the dirty work for you. Take a look at perldoc perlboot . It's what I used to understand how object oriented Perl works. You don't even have to create a full separate module. The object definitions can live in the same Perl script.

Using Object Oriented Perl keeps the mess outside your main program and makes it easier to maintain your program. Plus, if you have to modify your structure, you don't have to search your entire code to find all the places to change. The syntactic sugar makes it easier to see where you're going with your structure.

Compare this monstrosity with this object oriented monstrosity . Both programs do the same thing. I wrote the first one long ago, and found it so hard to maintain that I rewrote it from scratch in an object oriented style. (They're pre-commit hooks for Subversion in case anyone is wondering).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM