in reply to How to Check Hashes for Missing Items when Keys can be Values and vice versa

Personally I would probably build a hash of hashes, plus its inverse (no problem if the input data isn't too big):

use warnings; use strict; my (%sites,%dsks); while (<DATA>) { my ($site, $buf) = split /,/; for (split /:/, $buf) { for my $dsk (grep {$_!=0} /([0-9]+)!([0-9]+)!$/) { $sites{$site}{$dsk}++; $dsks{$dsk}{$site}++; } } } for my $dsk (sort {$a<=>$b} keys %dsks) { print "Dsk $dsk: ", join(", ", sort {$a<=>$b} keys %{ $dsks{$dsk} } ), "\n"; } for my $site (sort {$a<=>$b} keys %sites) { print "Site $site: ", join(", ", sort {$a<=>$b} keys %{ $sites{$site} } ), "\n"; }

As for your question about missing values, that's definitely a case of TIMTOWTDI. See for example the FAQ How do I compute the difference of two arrays? How do I compute the intersection of two arrays? You can also just iterate over the list of expected keys and check their existence in the target hash via exists, but that's the brute force method. There's also a trick I sometimes like to use that involves deleteing a hash slice (again, only if the input data isn't too big, because it's not the most efficient method), here I'll demonstrate by listing the "dsk"s (disks?) that are missing from each site:

my %alldsks = map {$_=>1} keys %dsks; for my $site (sort {$a<=>$b} keys %sites) { my @sitedsks = keys %{ $sites{$site} }; my %missingdsks = %alldsks; delete @missingdsks{@sitedsks}; print "Site $site MISSING: ", join(", ", sort {$a<=>$b} keys %missingdsks), "\n"; } __END__ Site 377 MISSING: 70, 71, 90, 91, 92, 93, 189, 190, 204, 205, 206, 207 +, 550, 551, 554 Site 512 MISSING: 71, 96, 97, 204, 205, 206, 207, 550, 551, 554 Site 587 MISSING: 70, 71, 90, 91, 92, 93, 96, 97, 189, 190, 204, 205, +206, 207 Site 1108 MISSING: 90, 91, 92, 93, 96, 97, 189, 190, 550, 551, 554

I'd also recommend you play it safer and Use strict and warnings.