Reputation: 8970
I have these 2 hashes below. First one is a template, second one is the users settings.
I need to be able to create a loop that goes over the second hash and if it finds a difference (a value exists in the first one but not the second) then it needs to do something with that key & value (lets just say print it)
$VAR1 = {
'Hotkeys' => [
'key',
'keyCR',
'keyHighlight',
'updated'
],
'Actions' => [
'action',
'actionCR',
'actionHighlight'
],
'Settings' => [
'chbAcronym',
'chbCompleted'
],
'NewSetting' => [
'NewValue'
]
};
$VAR1 = {
'Hotkeys' => [
'key',
'keyCR',
'keyHighlight'
],
'Actions' => [
'action',
'actionCR',
'actionHighlight'
],
'Settings' => [
'chbAcronym',
'chbCompleted'
]
};
Upvotes: 1
Views: 8030
Reputation: 6204
If I understand your issue correctly, you want to add items from the template hash to the user settings' hash only if those items from the template hash do not exist within the users' settings hash.
We can take advantage of Perl's Autovivification which will create the complete data structure within the user settings' hash if an item in that hash doesn't exist. Consider the following:
use strict;
use warnings;
use Data::Dumper;
my %template = (
'Hotkeys' => [ 'key', 'keyCR', 'keyHighlight', 'updated' ],
'Actions' => [ 'action', 'actionCR', 'actionHighlight' ],
'Settings' => [ 'chbAcronym', 'chbCompleted' ],
'NewSetting' => [ 'NewValue' ]
);
my %userSettings = (
'Hotkeys' => [ 'key', 'keyCR', 'keyHighlight' ],
'Actions' => [ 'action', 'actionCR', 'actionHighlight' ],
'Settings' => [ 'chbAcronym', 'chbCompleted', 'aUserSetting' ]
);
updateUserSettings( \%template, \%userSettings );
print Dumper \%userSettings;
sub updateUserSettings {
my ( $templateHash, $settingsHash ) = @_;
for my $key ( keys %$templateHash ) {
$settingsHash->{$key}->[$_] //= $templateHash->{$key}->[$_]
for 0 .. $#{ ${$templateHash}{$key} };
}
}
Output (a dump of %userSettings
after the 'update'):
$VAR1 = {
'Hotkeys' => [
'key',
'keyCR',
'keyHighlight',
'updated'
],
'Actions' => [
'action',
'actionCR',
'actionHighlight'
],
'NewSetting' => [
'NewValue'
],
'Settings' => [
'chbAcronym',
'chbCompleted',
'aUserSetting'
]
}
Note that %userSettings
is only updated with missing %template
information and nothing else is disturbed.
The subroutine updateUserSettings
uses Perl's defined-or (//=
) operator as it iterates through all the keys of %template
, so %userSettings
isn't changed if a key/value already exists, otherwise it updated.
Hope this helps!
Upvotes: 1
Reputation: 3406
Assuming that the first hash (template) contains all the possible values that the second can have, you can use this approach, which may not be the most efficient but is simple and doesn't require external modules:
use strict;
my $template = {
'Hotkeys' => [
'key',
'keyCR',
'keyHighlight',
'updated'
],
'Actions' => [
'action',
'actionCR',
'actionHighlight'
],
'Settings' => [
'chbAcronym',
'chbCompleted'
],
'NewSetting' => [
'NewValue'
]
};
my $user = {
'Hotkeys' => [
'key',
'keyCR',
'keyHighlight'
],
'Actions' => [
'action',
'actionCR',
'actionHighlight'
],
'Settings' => [
'chbAcronym',
'chbCompleted'
]
};
#take all user keys so that we don't perform a join in each iteration
my $all_user_keys = join(' ',keys %$user);
#loop all template keys to see what's missing from user keys
foreach my $template_key( keys %$template ) {
#this will return a true value if the template key also exists in user hash
my $key_exists_in_user = ( $all_user_keys =~ m/$template_key/ );
#if it exists, perform a second loop for the values of the array
if ($key_exists_in_user) {
#take all user values so that we don't perform a join in each iteration
my $all_user_values_of_key = join(' ', @{$user->{$template_key}});
#loop all values of template key, to see what's missing from user values
foreach my $template_key_value (@{$template->{$template_key}}) {
#if value is not found, do what you want with it
unless( $all_user_values_of_key =~ m/$template_key_value/ ) {
print " -- value '$template_key_value' does not exist in user key '$template_key'. will add it now\n";
push @{$user->{$template_key}}, $template_key_value;
}
}
#else, hash key is not found, so do what you want with it
} else {
print "hash key '$template_key' does not exist in user settings. Will add it now\n";
$user->{$template_key} = $template->{$template_key};
}
}
I used your example hashes and I also assumed that your hashes are actually hashrefs (copy pasted as they were)
Upvotes: 0
Reputation: 9109
Iterate over the keys and compare the arrays of matching keys.
sub my_special_hash_diff {
my(%hash_A,%hash_B) = (@_) ; #This may need some tweaking, you probably need
# need to pass in Hash references.
for $key ( keys %hash_B ) {
@array1 = $hash_B{$key} ;
@array2 = $hash_A{$key} ;
compare_2_arrays(@array1,@array2) ; # See answer below.
}
}
How to compare two perl arrays
Difference of Two Arrays Using Perl
Upvotes: 1