http://www.perlmonks.org?node_id=985783

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

Hello esteemed Monks,

I'm stuck yet again and need some assistance. As usual, it's my nemesis - Hashes. Why I battle with these as much as I do is really beyond me - it's certainly not a lack of reading FAQ's, PerlDOCs and all manner of other sources of information.
Essentially, I'm trying to write to a database, all the items of software on a z/OS mainframe. Thanks to some help from you all (Help with attributes and XML::Simple) I have managed to progress to the following hash:
$VAR1 = { 'Product' => { 'Unicenter CA-Deliver Output Management' => { 've +ndorUniqueKeyRef' => 'CA', 'sw +UniqueKey' => 'RMO', 'de +scription' => 'Unicenter CA-Deliver Output Management' }, 'Unicenter CA-JCLCheck Utility' => { 'vendorUniqu +eKeyRef' => 'CA', 'swUniqueKey +' => 'JCLCHECK', 'description +' => 'Unicenter CA-JCLCheck Utility' }, 'EREP Environmental Recording Edit Print' => { 'v +endorUniqueKeyRef' => 'IBM', 's +wUniqueKey' => 'EREP', 'P +roductVersion' => { + 'version' => '3', + 'swUniqueKey' => '5658-260', + 'name' => 'EREP Environmental Recording Edit Print' +, + 'versionNumber' => '03' + }, 'd +escription' => 'EREP Environmental Recording Edit Print' }, 'SYSQL' => { 'vendorUniqueKeyRef' => 'SPLWDGRP', 'swUniqueKey' => 'SYSQL', 'ProductVersion' => { 'ProductVersionR +elease' => { + 'releaseNumber' => '01', + 'swUniqueKey' => 'SYSQL-21', + 'name' => 'SYSQL', + 'release' => '1' + }, 'version' => '2' +, 'swUniqueKey' => + 'SYSQL-2', 'name' => 'SYSQL +', 'versionNumber' +=> '02' }, 'description' => 'SYSQL' }, '3270-PC File Transfer Program' => { 'vendorUniqu +eKeyRef' => 'IBM', 'swUniqueKey +' => '3270PCFT', 'description +' => '3270-PC File Transfer Program' }, 'Tivoli OMEGAMON XE on z/OS' => { 'vendorUniqueKe +yRef' => 'IBM', 'swUniqueKey' = +> 'OMXEZO', 'ProductVersion +' => { + 'version' => '3', + 'swUniqueKey' => '5698-A59', + 'name' => 'Tivoli OMEGAMON XE on z/OS', + 'versionNumber' => '03' + }, 'description' = +> 'Tivoli OMEGAMON XE on z/OS' }, 'Tivoli OMEGAMON XE for Messaging for z/OS' => +{ +'vendorUniqueKeyRef' => 'IBM', +'swUniqueKey' => 'OMXEMES', +'description' => 'Tivoli OMEGAMON XE for Messaging for z/OS' }, 'DB2 Utilities Suite for z/OS' => { 'vendorUnique +KeyRef' => 'IBM', 'swUniqueKey' + => 'DB2UTSU', 'ProductVersi +on' => { + 'DB2 Utilities Suite for z/OS' => { + 'swUniqueKey' => '5655-N97', + 'version' => '9', + 'versionNumber' => '09' + }, + 'DB2 Utilities Suite' => { + 'swUniqueKey' => '5697-E98', + 'version' => '7', + 'versionNumber' => '07' + } + }, 'description' + => 'DB2 Utilities Suite for z/OS' }, 'UMB' => { 'vendorUniqueKeyRef' => 'CSC', 'swUniqueKey' => 'CSCUMB', 'description' => 'UMB' } } };

Initially, all was good and I had the following:
my $sw = $xmldoc->{'Catalog'}->{'Products'}; my %sw = %{ $sw->{'Product'}}; foreach my $product (keys %sw) { print "Now processing $product\n"; my $version; my $release; my $description = $sw{$product}{'description'}; my $vendorUniqueKeyRef = $sw{$product}{'vendorUniqueKeyRef'}; my $swUniqueKey = $sw{$product}{'swUniqueKey'}; if ($sw{$product}{'ProductVersion'}) { $version = $sw{$product}{'ProductVersion'}{'version'}; if ($sw{$product}{'ProductVersion'}{'ProductVersionRelease'}) +{ $release = $sw{$product}{'ProductVersion'}{'ProductVersion +Release'}{'release'}; } else { $release = 0; } } else { $version = 0; $release = 0; } my $fullVersion = "$version.$release"; print " ***************\n The product is: $product\n The description is: $description\n The vendorUniqueKeyRef is: $vendorUniqueKeyRef\n The ProductVersion is: $fullVersion\n The swUniqueKey is: $swUniqueKey\n ***************\n"; };

However, I kept getting an error about using uninitialised variables while using strict. I realised that some software had versions like ".2" instead of "2.2" and then saw that I was not properly handling the fact that some products are installed twice with different versions, something which I had stupidly not catered for. I tried to fix that, only to find that not all version versions have a release, which I had tried to cater for, but only if they had been installed once.......
I've read up a load on getting data out of a HoH as well as an AoH, but I can't come right with this.
Essentially I'm trying to get a listing of all the software installed out of this hash into the database using DBD::ODBC (which I already have working for the rest of my program) whether it has a version, multiple versions, versions and releases, multiple versions and no releases, multiple vers...... Well you get the idea.......
I'd appreciate any help that anybody could give as weel as any advice on my current style and error checking.

Regards,
Smaug.
Peddle faster monkeys!! I need more power!!

Replies are listed 'Best First'.
Re: Iterative HOH or AOH?
by hbm (Hermit) on Aug 06, 2012 at 19:18 UTC

    You have a few choices.

    • Give all your variables default values each time you enter the for-loop; and reassign if the corresponding hash keys exist;
    • Or assign each variable the corresponding hash value or some default value (e.g., $version = (exists $sw{$product}{'ProductVersion'}{'version'} ? $sw{$product}{'ProductVersion'}{'version'} : 0););
    • Or use a similar construct when printing (e.g., print "Version: " . ($version ? $version : 0););
    • Or, turn off the warnings with no warnings 'uninitialized';.
Re: Iterative HOH or AOH?
by ig (Vicar) on Aug 07, 2012 at 07:24 UTC

    In addition to the issue of assigning default values in the cases where your input data provides none, you also have the issue that your input data has a variable organization. The value of the ProductVersion key might be a reference to an anonymous hash containing the details of a single product version or it might be a reference to an anonymous hash with a key for each of several product versions, where each key is the 'name' of the product version and each value is a reference to an anonymous hash containing the details of that product version (except for the name field, which is the key value).

    OK, even I can't read that, and I like words. What I would do is normalize the structure, then process that, maybe like this:

    foreach my $product (keys %sw) { print "Now processing $product\n"; my $version = '0'; my $release = '0'; my $description = $sw{$product}{'description'}; my $vendorUniqueKeyRef = $sw{$product}{'vendorUniqueKeyRef'}; my $swUniqueKey = $sw{$product}{'swUniqueKey'}; if ($sw{$product}{ProductVersion}) { if(exists($sw{$product}{ProductVersion}{name})) { # Push the immediate hash down, keyed by name # ignoring the duplicate name field $sw{$product}{ProductVersion} = { $sw{$product}{ProductVersion}{name} => $sw{$product}{P +roductVersion} }; } foreach my $product_version_name (keys %{$sw{$product}{Product +Version}}) { if($sw{$produt}{ProductVersion}{$product_version_name}{Pro +ductReleaseVersion}) { if(exists($sw{$produt}{ProductVersion}{$product_versio +n_name}{ProductReleaseVersion}{name})) { # Push the immediate hash down, keyed by name # ignoring the duplicate name field $sw{$produt}{ProductVersion}{$product_version_name +}{ProductReleaseVersion} = { $sw{$produt}{ProductVersion}{$product_version_ +name}{ProductReleaseVersion}{name} => $sw{$produt}{ProductVersion}{$product_vers +ion_name}{ProductReleaseVersion}; }; } foreach my $product_version_release_name (keys %{$sw{$ +product}{ProductVersion}{$product_version_name}{ProductReleaseVersion +}}) { # Print / store a record for the case that there i +s both a ProductVersion and ProductReleaseVersion } } else { # Print / store a record for the case that there is a +ProductVersion but no ProductReleaseVersion } } } else { # Print / store a record for the case that there is no Product +Version (and, therefore, no nested ProductReleaseVersion) } };

    I haven't actually printed the output. I would probably do that with some subroutines to avoid duplication, and set defaults if values are missing.

Re: Iterative HOH or AOH?
by choroba (Cardinal) on Aug 06, 2012 at 20:01 UTC
    Crossposted at StackOverflow. It is considered polite to inform about crossposting so people not attending the other site do not waste their time solving a problem already solved at the other end of the internet.
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ