I have a data structure like this one (with many hashes & arrays, containing different environments, users, profiles, access rights etc.) :
%AR = (
'prod' => {
'username' => {
'access_profile1' => [
'accessright1',
'accessright2',
'accessright3',
'accessrightN',
],
'access_profile2' => [
'accessright7',
'accessright8',
'accessright9',
],
'access_profileN' => [
'accessrightX',
'accessrightX',
'accessrightX',
],
},
},
);
I would like to give my script an option that, when enabled, it must force all profile names with a standard one (e.g. default_profile) in the structure (regenerate it) - of course this would mean that all accessrights are unique otherwise some might override others -
I know that I could do it by simply looping over this structure and filling a "new" structure, like this:
my $newAR;
foreach my $env ( keys %AR ) {
foreach my $user ( keys %{ $AR{$env} } ) {
foreach my $profile ( keys %{ $AR{$env}{$user} } ) {
# fill the new data structure
foreach my $accessright (@{ $AR{$env}{$user}{$profile} }) {
push (@{ $newAR{$env}{$user}{'default_profile'} }, $accessright);
}
}
}
}
which at the end gives the correct result:
$VAR1 = {
'prod' => {
'username' => {
'default_profile' => [
'accessrightX',
'accessrightX',
'accessrightX',
'accessright7',
'accessright8',
'accessright9',
'accessright1',
'accessright2',
'accessright3',
'accessrightN'
]
}
}
};
Isn't there a "cleaner" way of doing this in Perl? I am not very strong in all mappings, etc., and I am sure there would be a neater way of writing these nested loops.
for ( values %AR ) {
for my $to_fix ( values %$_ ) {
%$to_fix = (
default_profile => [
map { @$_ }
values %$to_fix
]
);
}
}
values %$to_fix
returns the array refs.map { @$_ } …
flattens them into access rights.[ … ]
stores them into an array.%$to_fix = ( default_profile => … )
replaces the existing contents with the new content.Your code does not place the access rights in any particular order, and neither does mine. Your key names does suggest an order, however. If you want the access rights ordered according to the original key names, you can use something like the following:
use Sort::Key::Natural qw( natsort );
%$to_fix = (
default_profile => [
map { $to_fix->{ $_ }->@* }
natsort
keys %$to_fix
]
);
Alternatively, it might make more sense to remove duplicate access rights and/or sort them by name.
use List::Util qw( uniq );
use Sort::Key::Natural qw( natsort );
%$to_fix = (
default_profile => [
natsort # Sorts the rights. Optional.
uniq # Removes duplicates. Optional.
map { @$_ }
values %$to_fix
]
);