jhisey has asked for the wisdom of the Perl Monks concerning the following question:

I have a MySQL database with data in it that I'll call rules. It's basically a map for a perl program that finds a rule based on the results of the previous rule. For example, rule 1 is run and based on the output (return) and the stages, the table is used to determine the next rule to run and stages to "be in." Here is a very simple example of the table:
* return * rule * stage1 * stage2 * next_rule * next_stage1 * next_stage2 *
***************************************************************************
*   ret1 *    1 *     A  *     00 *         2 *           B *          10 *
*   ret2 *    1 *     A  *     00 *         5 *           D *          23 *
*   bob1 *    2 *     B  *     10 *         5 *           D *          23 *
*   bob2 *    2 *     B  *     10 *         6 *           E *          44 *
*   dog1 *    3 *     B  *     10 *         6 *           E *          44 *
*   dog2 *    4 *     C  *     10 *         6 *           E *          44 *
*   cat1 *    5 *     D  *     23 *         7 *           F *          55 *
*   cat2 *    5 *     D  *     23 *         7 *           F *          56 *
*   cat3 *    5 *     D  *     23 *         7 *           F *          57 *
*   hit1 *    6 *     E  *     44 *         7 *           G *          55 *
*   hit1 *    6 *     K  *     14 *         7 *           H *          55 *
*   hit2 *    6 *     E  *     44 *         7 *           G *          56 *
*   etc.
***************************************************************************
What I'd like to do is create a representation of the tree and the different possible paths. I was thinking I could use a hash and each set of possible returns would be another level in the hash. Let's say I start off running rule 1 in stage1 A and stage2 00. This is the hash I'm trying to get to:
$hash{ret1}             = { rule => '1', stage1 => 'A', stage2 => '00', next_rule => '2', next_stage1 => 'B', next_stage2 => '10' };
$hash{ret1}{bob1}       = { rule => '2', stage1 => 'B', stage2 => '10', next_rule => '5', next_stage1 => 'D', next_stage2 => '23' };
$hash{ret1}{bob1}{cat1} = { rule => '5', stage1 => 'D', stage2 => '23', next_rule => '7', next_stage1 => 'F', next_stage2 => '55' };
$hash{ret1}{bob1}{cat2} = { rule => '5', stage1 => 'D', stage2 => '23', next_rule => '7', next_stage1 => 'F', next_stage2 => '56' };
$hash{ret1}{bob1}{cat3} = { rule => '5', stage1 => 'D', stage2 => '23', next_rule => '7', next_stage1 => 'F', next_stage2 => '57' };
$hash{ret1}{bob2}       = { rule => '2', stage1 => 'B', stage2 => '10', next_rule => '6', next_stage1 => 'E', next_stage2 => '44' };
$hash{ret1}{bob2}{hit1} = { rule => '6', stage1 => 'E', stage2 => '44', next_rule => '7', next_stage1 => 'G', next_stage2 => '55' };
$hash{ret1}{bob2}{hit2} = { rule => '6', stage1 => 'E', stage2 => '44', next_rule => '7', next_stage1 => 'G', next_stage2 => '56' };
$hash{ret2}             = { rule => '1', stage1 => 'A', stage2 => '00', next_rule => '5', next_stage1 => 'D', next_stage2 => '23' };
$hash{ret2}{cat1}       = { rule => '5', stage1 => 'D', stage2 => '23', next_rule => '7', next_stage1 => 'F', next_stage2 => '55' };
$hash{ret2}{cat2}       = { rule => '5', stage1 => 'D', stage2 => '23', next_rule => '7', next_stage1 => 'F', next_stage2 => '56' };
$hash{ret2}{cat3}       = { rule => '5', stage1 => 'D', stage2 => '23', next_rule => '7', next_stage1 => 'F', next_stage2 => '57' };
These trees will get up to 20 elements deep. I was thinking I could just keep going through the last set of keys, grab the rows from the table and append the next set of keys. The problem is I can't figure out how to use that last set. If I've traversed down to $hash{ret1}{bob1}, I'll select from my table all rows where rule => '5', stage1 => 'D', and stage2 => '23' and get cat1, cat2, and cat3. How do I say "ok, now create $hash{ret1}{bob1}{cat1}, $hash{ret1}{bob1}{cat2}, and $hash{ret1}{bob1}{cat3}?

Hopefully what I'm trying to do makes sense. If you have a better way to create a structure of this data that I can then use to print or diagram with, that's great too. This is just the way I thought the result would make for an easily traversed "tree" when done.

Replies are listed 'Best First'.
Re: Getting hash key tree
by haukex (Archbishop) on Nov 27, 2018 at 20:48 UTC
Re: Getting hash key tree
by NetWallah (Canon) on Nov 28, 2018 at 18:56 UTC
    There are several "Finite state machine" implementations on CPAN (Search 'fsm').

    This one seems to meet your use case: FSM::Tiny, and appears to be pure perl without dependencies and dependent only on Class::Accessor::Lite, which appears to be free of other dependencies.

                    Memory fault   --   brain fried

Re: Getting hash key tree
by tybalt89 (Monsignor) on Nov 28, 2018 at 20:44 UTC

    Something like this?

    #!/usr/bin/perl # https://perlmonks.org/?node_id=1226425 use strict; use warnings; my %hash; my ($header, @rules) = grep /\w/, <DATA>; my (undef, @fieldnames) = $header =~ /\w+/g; findnext(\%hash, 1, 'A', '00'); use Data::Dump 'dd'; dd \%hash; sub findnext { my ($refh, $rule, $stage1, $stage2) = @_; for my $row (@rules) { if($row =~ /^\W+(\w+)\W+$rule\W+$stage1\W+$stage2\W+(\w+)\W+(\w+)\ +W+(\w+)/) { my ($return, $nextrule, $nextstage1, $nextstage2) = ($1, $2, $3, + $4); my %values; @values{@fieldnames} = ($rule, $stage1, $stage2, $nextrule, $nextstage1, $nextstage2) +; $refh->{$return}{ROWDATA} = \%values; findnext($refh->{$return}, $nextrule, $nextstage1, $nextstage2); } } } __DATA__ * return * rule * stage1 * stage2 * next_rule * next_stage1 * next_sta +ge2 * ********************************************************************** +***** * ret1 * 1 * A * 00 * 2 * B * + 10 * * ret2 * 1 * A * 00 * 5 * D * + 23 * * bob1 * 2 * B * 10 * 5 * D * + 23 * * bob2 * 2 * B * 10 * 6 * E * + 44 * * dog1 * 3 * B * 10 * 6 * E * + 44 * * dog2 * 4 * C * 10 * 6 * E * + 44 * * cat1 * 5 * D * 23 * 7 * F * + 55 * * cat2 * 5 * D * 23 * 7 * F * + 56 * * cat3 * 5 * D * 23 * 7 * F * + 57 * * hit1 * 6 * E * 44 * 7 * G * + 55 * * hit1 * 6 * K * 14 * 7 * H * + 55 * * hit2 * 6 * E * 44 * 7 * G * + 56 * ********************************************************************** +*****

    Outputs:

    { ret1 => { bob1 => { cat1 => { ROWDATA => { next_rule => 7, next_stage1 => "F", next_stage2 => 55, rule => 5, stage1 => "D", stage2 => 23, }, }, cat2 => { ROWDATA => { next_rule => 7, next_stage1 => "F", next_stage2 => 56, rule => 5, stage1 => "D", stage2 => 23, }, }, cat3 => { ROWDATA => { next_rule => 7, next_stage1 => "F", next_stage2 => 57, rule => 5, stage1 => "D", stage2 => 23, }, }, ROWDATA => { next_rule => 5, next_stage1 => "D", next_stage2 => 23, rule => 2, stage1 => "B", stage2 => 10, }, }, bob2 => { hit1 => { ROWDATA => { next_rule => 7, next_stage1 => "G", next_stage2 => 55, rule => 6, stage1 => "E", stage2 => 44, }, }, hit2 => { ROWDATA => { next_rule => 7, next_stage1 => "G", next_stage2 => 56, rule => 6, stage1 => "E", stage2 => 44, }, }, ROWDATA => { next_rule => 6, next_stage1 => "E", next_stage2 => 44, rule => 2, stage1 => "B", stage2 => 10, }, }, ROWDATA => { next_rule => 2, next_stage1 => "B", next_stage2 => 10, rule => 1, stage1 => "A", stage2 => "00", }, }, ret2 => { cat1 => { ROWDATA => { next_rule => 7, next_stage1 => "F", next_stage2 => 55, rule => 5, stage1 => "D", stage2 => 23, }, }, cat2 => { ROWDATA => { next_rule => 7, next_stage1 => "F", next_stage2 => 56, rule => 5, stage1 => "D", stage2 => 23, }, }, cat3 => { ROWDATA => { next_rule => 7, next_stage1 => "F", next_stage2 => 57, rule => 5, stage1 => "D", stage2 => 23, }, }, ROWDATA => { next_rule => 5, next_stage1 => "D", next_stage2 => 23, rule => 1, stage1 => "A", stage2 => "00", }, }, }