Reputation: 7097
If I have an array like below, where the order of the elements are random and there are no limit on how many levels each element can have. Here only show 3 levels. a
, b
, and c
.
I would like to be able to parse such arrays, and store the result in a hash of hash like so, when there are only 3 levels
$VAR1 = {
'a' => {
'b' => 'c'
}
};
Question
My problem is how to write a regex for this, because the first level doesn't have a /
at the end, and since the order of the elements is random, if a/b/c
have already been inserted in the hash, then the element a
shouldn't delete the hash values for key a
.
How so solve such a problem?
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my @ar = ("a", "a/b", "a/b/c");
my %h = ();
foreach my $a (@ar) {
}
Upvotes: 1
Views: 137
Reputation: 98398
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use Data::Diver 'DiveRef';
my @array = ("a", "a/b", "a/b/c");
my %hash = ();
foreach my $element (@array) {
DiveRef( \%hash, \( split /\//, $element ) );
}
Normally you would assign to or modify the scalar reference returned by DiveRef (or assign to the result of DiveVal), but here you just want to make sure the key exists.
The \(...)
turns the list returned by split into a list of scalar references, which tells Data::Diver these are always going to be hash keys, not possibly array indexes if they are numeric.
Upvotes: 2
Reputation: 386331
To get
"a" => $tree = "a";
"a", "a/b" => $tree = { "a" => "b" };
"a", "a/b", "a/b/c" => $tree = { "a" => { "b" => "c" } };
"a", "a/b", "a/c" => $tree = { "a" => { "b" => undef, "c" => undef } };
Code:
my $tree;
for ("a", "a/b", "a/b/c") {
my @keys = split qr{/};
my $val = pop(@keys);
my $p = \$tree;
while (@keys) {
my $key = shift(@keys);
$$p = { $$p => undef } if !ref($$p);
$p = \( ($$p)->{$key} );
}
if (defined($$p)) {
$$p = { $$p => undef } if !ref($$p);
($$p)->{$val} = undef;
} else {
$$p = $val;
}
}
But that's not a good data structure since you need to use ref
to navigate it. Instead, I recommend
"a" => $tree = { "a" => undef };
"a", "a/b" => $tree = { "a" => { "b" => undef } };
"a", "a/b", "a/b/c" => $tree = { "a" => { "b" => { "c" => undef } } };
"a", "a/b", "a/c" => $tree = { "a" => { "b" => undef, "c" => undef } };
Code:
my $tree;
for ("a", "a/b", "a/b/c") {
my $p = \$tree;
$p = \( ($$p)->{$_} ) for split qr{/};
}
See how much simpler it is to build this data structure? You'll get similar benefits when you try to navigate it.
Note that you could use Data::Diver to create the second data structure (though I recall it being much slower).
use Data::Diver qw( DiveRef );
my $tree;
for ("a", "a/b", "a/b/c") {
DiveRef($tree //= {}, \split(qr{/}));
}
Upvotes: 2
Reputation: 126
An example:
my @ar = ("a", "a/b", "a/b/c");
my %h;
for (@ar) {
my $levels = split /\//;
for (my $i = 0; $i <= $#levels; $i++) {
# How ever you want to set-up your hash of hashes
# But each for loop iteration would give you a "level" of input
}
}
Upvotes: 0