We're starting from the top of tree, searching node with cat_parent_id = '', and then we descend deeper and search chidren of that found parent (cat_parent_id = '$k'), returning array of found children from top to bottom, so in the end you have nice array that you join or use it other way.
use strict;
use v5.10;
my $hash = {
'986172' => {
'cat_parent_id' => '',
'cat_name' => 'Category1'
},
'986178' => {
'cat_parent_id' => '986177',
'cat_name' => 'Category4'
},
'986177' => {
'cat_parent_id' => '986176',
'cat_name' => 'Category3'
},
'986176' => {
'cat_name' => 'Category2',
'cat_parent_id' => '986172'
}
};
sub rec {
my ($hash, $parent) = @_;
my @str;
foreach my $k (keys %{ $hash } ) {
if ($hash->{$k}->{cat_parent_id} eq $parent) {
push @str, $hash->{$k}->{cat_name};
push @str, rec($hash, $k);
last;
}
}
return @str;
}
my @arr = rec($hash, '');
say join(',', @arr);
UPDATE:
Slightly changed recursive function so it won't check categories that was already $seen as parents. We don't know if we could just delete $hash->{$k} after we found it and extract cat_name, $hash could be needed later, so it's only grep { not exists $seen->{$_}}
sub rec {
my ($hash, $parent, $seen) = @_;
my @str;
foreach my $k (grep { not exists $seen->{$_} } keys %{ $hash } ) {
if ($hash->{$k}->{cat_parent_id} eq $parent) {
push @str, $hash->{$k}->{cat_name};
$seen->{$k} = 1;
push @str, rec($hash, $k, $seen);
last;
}
}
return @str;
}