audioboxer has asked for the wisdom of the Perl Monks concerning the following question:
Hi Everyone - I am iterating through data to build this below hash. Now using the keys and "cat_parent_id" I need to create a string from this looks like "Category1,Category2,Category3,Category4". I am at a loss and hoping someone can help. Thank you.
$VAR1 = {
'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'
}
};
Re: Organizing data from a hash
by jdporter (Canon) on Dec 01, 2020 at 18:26 UTC
|
Using a module is usually a good idea. But for fun, here's my hand-rolled solution.
my $ds = {
'986172' => { 'cat_parent_id' => '', 'cat_name' => 'Category
+1', },
'986178' => { 'cat_parent_id' => '986177', 'cat_name' => 'Category
+4', },
'986177' => { 'cat_parent_id' => '986176', 'cat_name' => 'Category
+3', },
'986176' => { 'cat_parent_id' => '986172', 'cat_name' => 'Category
+2', }
};
# invert the structure:
my $sd = { map { $ds->{$_}{'cat_parent_id'} => { 'cat_child_id' => $_,
+ 'cat_name' => $ds->{$_}{'cat_name'} } } keys %$ds };
sub foo # recursive
{
local $_ = shift || '';
exists $sd->{$_} ? foo( $sd->{$_}{'cat_child_id'}, @_, $sd->{$_}{'
+cat_name'} ) : @_
}
print join ',', foo();
I reckon we are the only monastery ever to have a dungeon stuffed with 16 ,000 zombies.
| [reply] [d/l] |
Re: Organizing data from a hash (updated)
by haukex (Bishop) on Dec 01, 2020 at 17:13 UTC
|
my $string = join ",", sort map { $_->{cat_name} } values %$VAR1;
See also perldsc, perlreftut, and perlref.
Sorry, I missed the part about the parent ID. Update forthcoming, please hold.
Update: Ok, here's one of several ways to do it, using a topological sort, assuming this graph representation of your categories makes sense - you'd have to explain your data structure more otherwise.
use warnings;
use strict;
use Graph;
my $data = {
'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'
}
};
my $g = Graph->new(directed => 1);
for my $k (keys %$data) {
next unless $data->{$k}{cat_parent_id};
die unless exists $data->{ $data->{$k}{cat_parent_id} };
$g->add_edge($data->{$k}{cat_parent_id}, $k);
}
my $string = join ",", map { $data->{$_}{cat_name} }
$g->topological_sort;
| [reply] [d/l] [select] |
Re: Organizing data from a hash
by tybalt89 (Prior) on Dec 01, 2020 at 20:53 UTC
|
#!/usr/bin/perl
use strict;
use warnings;
use List::Util qw( uniq );
my $data =
{
986172 => { cat_name => "Category1", cat_parent_id => "" },
986176 => { cat_name => "Category2", cat_parent_id => 986172 },
986177 => { cat_name => "Category3", cat_parent_id => 986176 },
986178 => { cat_name => "Category4", cat_parent_id => 986177 },
};
my $string = join ',', find( keys %$data );
print "$string\n";
sub find
{
uniq map {
my $ref = $data->{$_};
$ref ? ( find( $ref->{cat_parent_id} ), $ref->{cat_name} ) : ()
} @_;
}
Or build a little prerequisite table and topologically extract the names one by one.
#!/usr/bin/perl
use strict;
use warnings;
my $data =
{
986172 => { cat_name => "Category1", cat_parent_id => "" },
986176 => { cat_name => "Category2", cat_parent_id => 986172 },
986177 => { cat_name => "Category3", cat_parent_id => 986176 },
986178 => { cat_name => "Category4", cat_parent_id => 986177 },
};
my $parents = '';
for my $v ( values %$data )
{
$parents .= $v->{cat_parent_id}
? $v->{cat_name} . ' ' . $data->{$v->{cat_parent_id}}{cat_name} .
+ "\n"
: $v->{cat_name} . "\n";
}
#print "$parents\n";
my @order;
$parents =~ s/\b($1)\b//g, push @order, $1 while $parents =~ s/^(\w+)\
+h*\n//m;
my $answer = join ',', @order;
print "$answer\n";
$parents and die "loop in parents at\n$parents";
| [reply] [d/l] [select] |
Re: Organizing data from a hash
by GrandFather (Saint) on Dec 01, 2020 at 20:23 UTC
|
use warnings;
use strict;
my $data = {
'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'
}
};
map{$data->{$data->{$_}{cat_parent_id}}{cat_child_id} = $_}
grep{$data->{$_}{cat_parent_id}} keys %$data;
my ($node) = grep{!$data->{$_}{cat_parent_id}} keys %$data;
my @chain = $data->{$node}{cat_name};
while ($data->{$node}{cat_child_id}) {
$node = $data->{$node}{cat_child_id};
push @chain, $data->{$node}{cat_name};
}
print join ',', @chain;
Prints:
Category1,Category2,Category3,Category4
Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
| [reply] [d/l] [select] |
Re: Organizing data from a hash
by alexander_lunev (Pilgrim) on Dec 01, 2020 at 21:03 UTC
|
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;
}
| [reply] [d/l] [select] |
Re: Organizing data from a hash
by kcott (Bishop) on Dec 02, 2020 at 06:22 UTC
|
G'day audioboxer,
Purely as a fun exercise, I put this together before studying other offerings.
There's a few similarities with other posts.
#!/usr/bin/env perl
use strict;
use warnings;
use constant {
CHILD_ID => 0,
CAT_NAME => 1,
};
my $VAR1 = {
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' }
};
my $wanted_string = get_wanted_string($VAR1);
print "$wanted_string\n";
sub get_wanted_string {
my ($data) = @_;
my (%family, @cats);
for my $child_id (keys %$data) {
my ($parent_id, $cat_name)
= @{$data->{$child_id}}{qw{cat_parent_id cat_name}};
$family{$parent_id} = [$child_id, $cat_name];
}
return join ',', @{recurse_family(\%family, \@cats)};
}
sub recurse_family {
my ($family, $cats, $parent) = @_;
$parent = '' unless defined $parent;
return $cats unless exists $family->{$parent};
push @$cats, $family->{$parent}[CAT_NAME];
recurse_family($family, $cats, $family->{$parent}[CHILD_ID]);
}
Output:
Category1,Category2,Category3,Category4
| [reply] [d/l] [select] |
|
|