Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Organizing data from a hash

by audioboxer (Acolyte)
on Dec 01, 2020 at 17:07 UTC ( #11124462=perlquestion: print w/replies, xml ) Need Help??

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' } };

Replies are listed 'Best First'.
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.
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;
Re: Organizing data from a hash
by tybalt89 (Prior) on Dec 01, 2020 at 20:53 UTC

    Fun little problem - just stick the parents in front of the children and clean it up at the end :)

    #!/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";
Re: Organizing data from a hash
by GrandFather (Saint) on Dec 01, 2020 at 20:23 UTC

    First add a child link to each node then traverse by child order:

    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
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; }
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

    — Ken

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://11124462]
Approved by marto
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (5)
As of 2021-01-25 18:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Notices?