<> kirjoitti 22.06.2005:
>
> I have a list that I read into an array. Each element is a text string
> like this (it's an object identifier):
>
> "region.level.location.system"
Okay, you can store this in a 4-dimensional hash like this:
my %hash;
foreach my $string (@array) {
my ($region, $level, $location, $system) = split /\./, $string;
$hash{$region}{$level}{$location}{$system}++;
}
The values in the hash will simply be numbers indicating how many
times each identifier has been seen. You don't need to use them for
anything -- it's just the keys that matter.
> need to generate a report that shows me the relationships in an orderly
> (almost graphic) manner ... I want to be able to print out something
> like this (actual formatting is not important, just the organization):
>
> region1
>
> region1.level1
> region1.level2
>
> region1.level1.location1
> region1.level1.location2
[snip]
>
> ... so on and so forth ... iterating through every region, every
> level, every location, every system
For the output you want, the simplest way would be to write four
separate loops, one for each depth. They will all look fairly
similar:
foreach my $region (sort keys %hash) {
print "$region\n";
}
foreach my $region (sort keys %hash) {
my %region = %{ $hash{$region} };
foreach my $level (sort keys %region) {
print "$region.$level\n";
}
}
foreach my $region (sort keys %hash) {
my %region = %{ $hash{$region} };
foreach my $level (sort keys %region) {
my %level = %{ $region{$level} };
foreach my $location (sort keys %level) {
print "$region.$level.$location\n";
}
}
}
foreach my $region (sort keys %hash) {
my %region = %{ $hash{$region} };
foreach my $level (sort keys %region) {
my %level = %{ $region{$level} };
foreach my $location (sort keys %level) {
my %location = %{ $level{$location} };
foreach my $system (sort keys %location) {
print "$region.$level.$location.$system\n";
}
}
}
}
Of course, if you wanted to do the same for a nested hash of arbitrary
depth, you could use a recursive solution:
sub keys_at_depth {
my ($hashref, $depth) = @_;
return sort keys %$hashref if $depth < 2;
my @keys;
foreach my $key (sort keys %$hashref) {
my $subhash = $hashref->{$key};
next unless ref $subhash eq 'HASH';
push @keys, map "$key.$_", keys_at_depth($subhash, $depth-1);
}
return @keys;
}
my $depth = 1;
while (my @keys = keys_at_depth(\%hash, $depth)) {
print "$_\n" for @keys;
$depth++;
}
There, that's much nicer, isn't it? Of course, there are several
other ways to approach your problem as well. For example, you could
instead use four separate hashes, one for each depth:
my (%regions, %levels, %locations, %systems);
foreach my $string (@array) {
my ($region, $level, $location, $system) = split /\./, $string;
$regions {"$region"}++;
$levels {"$region.$level"}++;
$locations{"$region.$level.$location"}++;
$systems {"$region.$level.$location.$system"}++;
}
print "$_\n" for sort keys %regions;
print "$_\n" for sort keys %levels;
print "$_\n" for sort keys %locations;
print "$_\n" for sort keys %systems;
Or you could use an array of hashes:
my @depths;
foreach my $string (@array) {
my @parts = split /\./, $string;
$depths[$_]{join ".", @parts[0 .. $_]}++ for 0 .. $#parts;
}
foreach my $depth (@depths) {
print "$_\n" for sort keys %$depth;
}
There, I don't think it gets any more compact than that. Of course,
to populate @array, you'd use:
my @array = <>;
chomp @array;
and don't forget to start your script with:
#!/usr/bin/perl
use warnings;
use strict;
--
Ilmari Karonen
To reply by e-mail, please replace ".invalid" with ".net" in address.