Click to See Complete Forum and Search --> : create hash inside a hash


Steven1987
09-14-2003, 03:29 PM
I have a hash inside a hash of menu items.
Each item of that hash has to conain another hash with submenu items. Example:

%menu = {
menu_for_admin = {
item1 => {
name => "Item 1",
subitems => [
{
name => "Subitem1.1",
url => "http://Domain.com/controlpanel/subitem1.1.htm",
},
],
bgcolor => "black",
forecolor => "yellow",
},
item2 => {
name => "Item 2",
subitems => [
{
name => "Subitem2.1",
url => "http://Domain.com/my/controlpanel/subitem2.1.htm",
},
],
bgcolor => "black",
forecolor => "yellow",
},
},

menu_for_guest = {
item1 => {
name => "Item 1",
subitems => [
{
name => "Subitem1.1",
url => "http://Domain.com/public/subitem1.1.htm",
},
],
bgcolor => "black",
forecolor => "yellow",
},
item2 => {
name => "Item 2",
subitems => [
{
name => "Subitem2.1",
url => "http://Domain.com/public/subitem2.1.htm",
},
],
bgcolor => "black",
forecolor => "yellow",
},
},
}

This code works fine. (or I must have made typos when entering the text here)
Now I have another hash wich contains more menu items with the same structure (instead of %menu this one is called %menu_extra).
menu_for_admin and menu_for_guest are defined in both hashes. item1 and item2 are always defined in the %menu hash, but not always in the %menu_extra hash. subitems from %menu are never defined in %menu_extra.
Is it possible to 'copy' %menu_extra into %menu_hash (you can compare it to copying folders with existing subfolders?

Jeff Mott
09-14-2003, 10:19 PM
There is no built-in function for this, and coding will take some work. Fortunately, though, I have nothing better to do with my time. ;)

I thought this complex enough to package into its own module. Feedback welcomed from other Perl gurus.

DataStruct/Merge.pmpackage DataStruct::Merge;
our $VERSION = v1.0;

use strict;
use warnings FATAL => 'all';
use Carp qw[carp croak];
use base qw[Exporter];
our @EXPORT = qw[merge REPLACE SKIP];

use constant REPLACE => 0;
use constant SKIP => 1;

sub merge
{
my($ref_1, $ref_2, $mode) = splice @_;
croak('Invalid mode') unless $mode == REPLACE or $mode == SKIP;
carp('First argument must be an array or hash reference') unless ref $ref_1 eq 'ARRAY' or ref $ref_1 eq 'HASH';
carp('Second argument must be an array or hash reference') unless ref $ref_2 eq 'ARRAY' or ref $ref_2 eq 'HASH';
carp('Arguments must have matching reference types') unless ref $ref_1 eq ref $ref_2;
return unless ref $ref_1 eq 'ARRAY' || ref $ref_1 eq 'HASH' and
ref $ref_2 eq 'ARRAY' || ref $ref_2 eq 'HASH' and
ref $ref_1 eq ref $ref_2;
if (ref $ref_1 eq 'ARRAY') {
for (my $i = 0; $i < @{$ref_2}; ++$i) {
if (ref ${$ref_1}[$i] eq 'ARRAY' || ref ${$ref_1}[$i] eq 'HASH' and ref ${$ref_1}[$i] eq ref ${$ref_2}[$i]) {
merge(${$ref_1}[$i], ${$ref_2}[$i], $mode);
}
elsif ($mode == REPLACE) {
${$ref_1}[$i] = ${$ref_2}[$i];
}
elsif ($mode == SKIP) {
${$ref_1}[$i] = ${$ref_2}[$i] unless defined ${$ref_1}[$i];
}
}
}
else {
for (keys %{$ref_2}) {
if (ref ${$ref_1}{$_} eq 'ARRAY' || ref ${$ref_1}{$_} eq 'HASH' and ref ${$ref_1}{$_} eq ref ${$ref_2}{$_}) {
merge(${$ref_1}{$_}, ${$ref_2}{$_}, $mode);
}
elsif ($mode == REPLACE) {
${$ref_1}{$_} = ${$ref_2}{$_};
}
elsif ($mode == SKIP) {
${$ref_1}{$_} = ${$ref_2}{$_} unless defined ${$ref_1}{$_};
}
}
}
return;
}

1;

Example usage:use DataStruct::Merge;
merge(\@CopyTo, \@CopyFrom, REPLACE);
merge(\@CopyTo, \@CopyFrom, SKIP);
merge(\%CopyTo, \%CopyFrom, REPLACE);
merge(\%CopyTo, \%CopyFrom, SKIP);The first two arguments to merge() must be both array references or both hash references. But all other values within can be a mix or arrays, hashes, or just plain scalars. If you have any other questions, let me know. I have a bad habit of not documenting my work.

Steven1987
09-15-2003, 03:34 PM
Thanks Jeff,

I hope one day I'll be just as good as you so I can help others out too.