CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Beginner:
Combining multi arrays from hash tree lvl

 



zohman
Novice

May 2, 2016, 5:04 AM

Post #1 of 6 (2046 views)
Combining multi arrays from hash tree lvl Can't Post

Hi,

i'm stuck with a problem where i need to merge all elements that
belong to arrays in the hash per hash key.

for example:

i have $hash like this:


Code
$VAR1 = {  
'lvl1' => {
'HashOfArrays1' => {
'array1' => [
'element1',
'element2',
'element3',
'element4'
],
'array2' => [
'element5',
'element6',
'element7',
'element8',
'element9',
'element10'
]
}
},
'lvl2' => {
'HashOfArrays1' => {
'HashOfArrays2' => {
'array1' => [
'element11',
'element12',
'element13',
'element14'
],
'HashOfArrays2-1' => {
'array1-1' => [
'element15',
'element16'
]
},
'array2' => [
'element17',
'element18'
]
}
}
}
};


the idea is to get @array to contain all the elements from lvl1
regardless with how deep the hash go's as long as it is on lvl1 and so on.

with my example, consider i have:
@lvls = ('lvl1','lvl2');

foreach my $lvl (@lvls) {
@array = $hash->{$lvl}
}
i want that @array contain all the elements in that lvl for every iteration,

so at the first iteration @array will be:


Code
@array = ('element1','element2','element3','element4','element5','element6','element7','element8','element9','element10');


at the second iteration @array will be:


Code
@array = ('element11','element12','element13','element14','element15','element16','element17','element18');


my goal is to have some element lets say $var=element4
i want to know if this $var exist in which lvl regardless of the deep of the tree.


Code
my @lvls = ('lvl1','lvl2'); 
my $var = 'element4';

foreach my $lvl (@lvls) {
my @array = $hash->{$lvl}; # all elements\values from that lvl
foreach my $element (@array) {
if ($var eq $element) {
# do something
}
}
}


i treid this with no luck:


Code
my @lvls = ('lvl1','lvl2'); 
my $var = 'element4';

foreach my $lvl (@lvls) {
my @elements = deep_diver($hash->{$lvl}->{'HashOfArrays1'}) if ($hash->{$lvl}->{'HashOfArrays1'});

# HashOfArrays1 is always present below first $lvl.

foreach my $element (@elements) {
if ($var eq $element) {
# do something
}
}
}

sub deep_diver {

my ($in, $out) = @_;
for my $key (%$in) {
my $value = $in->{$key};
if ( ref($value) eq 'HASH' ) {
deep_diver($value, $out);
}
else {
push (@m,@$value);
}
}
return \@m;
}


in my try, if i replace:
return \@m; with print "@m\n"; in the deep_diver subroutine,
all the elements are printed on the screen, but i can't seems to return it
to @elements outside the subroutine.. i think it try to return all
the arrays to the @elements array and not all the actually elements.

what am i missing here?

Thanks in advance!

Zohman.


Zhris
Enthusiast

May 2, 2016, 4:05 PM

Post #2 of 6 (2026 views)
Re: [zohman] Combining multi arrays from hash tree lvl [In reply to] Can't Post

Take a look at the first and third examples of Data::Rmap, it will save you the hassle of writing your own recursive routines when extracting and/or processing values from nested data structures.

Chris


(This post was edited by Zhris on May 2, 2016, 4:14 PM)


Laurent_R
Veteran / Moderator

May 3, 2016, 12:45 AM

Post #3 of 6 (2011 views)
Re: [zohman] Combining multi arrays from hash tree lvl [In reply to] Can't Post


Quote
my goal is to have some element lets say $var=element4
i want to know if this $var exist in which lvl regardless of the deep of the tree.


If your goal in the end is to test for value existence, then your output data structure should probably be a hash rather than an array, since it is much easier and much more efficient to test for the existence of something in a hash than in an array.

Compare:

Code
     foreach my $element (@array) {  
if ($var eq $element) {
# do something
}
}


with

Code
if (exists %hash{$var}) { 
# do something
}



Laurent_R
Veteran / Moderator

May 3, 2016, 1:55 AM

Post #4 of 6 (2009 views)
Re: [zohman] Combining multi arrays from hash tree lvl [In reply to] Can't Post

OK, two recursive traversal versions, just for fun. Wink

If I understood your requirement correctly, you want to end up with a list of arrays, with one array per first level of the hash. This could be something like this:


Code
use strict; 
use warnings;
use Data::Dumper;

my $deep_hash_ref = {
'lvl1' => {
'HashOfArrays1' => {
'array1' => [
'element1',
'element2',
'element3',
'element4'
],
'array2' => [
'element5',
'element6',
'element7',
'element8',
'element9',
'element10'
]
}
},
'lvl2' => {
'HashOfArrays1' => {
'HashOfArrays2' => {
'array1' => [
'element11',
'element12',
'element13',
'element14'
],
'HashOfArrays2-1' => {
'array1-1' => [
'element15',
'element16'
]
},
'array2' => [
'element17',
'element18'
]
}
}
}
};

my @final_output;

foreach my $sub_hash_ref (values %$deep_hash_ref) {
my @sub_output;
deep_diver($sub_hash_ref, \@sub_output);
push @final_output, [@sub_output];
}

sub deep_diver {
my ($in, $out) = @_;
if (ref $in eq 'HASH') {
for my $subref (values %$in) {
deep_diver($subref, $out);
}
} else {
push @$out, @$in;
}
}

print Dumper @final_output;


This will output the following:

Code
$ perl deep_diver.pl 
$VAR1 = [
'element11',
'element12',
'element13',
'element14',
'element15',
'element16',
'element17',
'element18'
];
$VAR2 = [
'element1',
'element2',
'element3',
'element4',
'element5',
'element6',
'element7',
'element8',
'element9',
'element10'
];

which is presumably what you are looking for.

This is an array of arrays, but, as I said in my previous post, this might not be the most practical data structure for your further processing.

If you decide to build instead a hash of hashes, which is probably better for your next processing steps, you could do this (same input data as above omitted for brevity):


Code
my %final_output;            

foreach my $sub_hash_key (keys %$deep_hash_ref) {
my %sub_output;
deep_diver($sub_hash_key, $deep_hash_ref->{$sub_hash_key});
# $final_output{$sub_hash_key} = {%sub_output};
}

sub deep_diver {
my ($sub_key, $in) = @_;
if (ref $in eq 'HASH') {
for my $subref (values %$in) {
deep_diver($sub_key, $subref);
}
} else {
$final_output{$sub_key}{$_} = 1 for @$in;
}
}

print Dumper \%final_output;


which now displays a HoH:

Code
$  perl deep_diver2.pl 
$VAR1 = {
'lvl2' => {
'element18' => 1,
'element11' => 1,
'element16' => 1,
'element15' => 1,
'element12' => 1,
'element17' => 1,
'element13' => 1,
'element14' => 1
},
'lvl1' => {
'element5' => 1,
'element4' => 1,
'element1' => 1,
'element6' => 1,
'element9' => 1,
'element10' => 1,
'element3' => 1,
'element7' => 1,
'element8' => 1,
'element2' => 1
}
};


I hope this helps.


zohman
Novice

May 3, 2016, 5:52 AM

Post #5 of 6 (2001 views)
Re: [Zhris] Combining multi arrays from hash tree lvl [In reply to] Can't Post


In Reply To
Take a look at the first and third examples of Data::Rmap, it will save you the hassle of writing your own recursive routines when extracting and/or processing values from nested data structures.

Chris


Thanks Chris,
i saw a little but i'll need to install the module in our environment and
it's a little bureaucracy to install this module in my environment :)
as i need it to run on multi sites around the world
and some with different perl versions, i'm searching for something generic.

Zohman.


zohman
Novice

May 3, 2016, 6:04 AM

Post #6 of 6 (2000 views)
Re: [Laurent_R] Combining multi arrays from hash tree lvl [In reply to] Can't Post

Excellent! Smile
first option working great!

i know hash will be better but not in my case,
everything i show you here is already done inside iteration of another hash
i don't need to buffer %final_result for later.. because i "undef" it with every iteration of it's parent loop,
i'm just checking if it exists, and raising a flag to the parent hash.
but you couldn't understand it from my example Wink

Thanks,
Zohman.

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives