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

Hi all, i have this csv file as input. With three columns of child ,parent & relation
child, Parent, relation M10, Q, P, M143, M10, P, M406, M143, PL, M407, M143, PL, M420, M143, E, M421, M143, E,
Now for each of the child I want their relations traced back to the parent 'Q'.

For example conside child 'M407' (3rd row) M406<-M143<-M10<-Q = P.P.PL

For child M10 (first row), Q is the parent, so M10<-Q = P

For last row M421 we have M421<-M143<-M10<-Q = P.P.E

I want such relations for all the 6 childs. Please help

Replies are listed 'Best First'.
Re: constructing a tree from csv file
by choroba (Cardinal) on Nov 01, 2013 at 09:49 UTC
    I'd use a hash to keep the structure:
    #!/usr/bin/perl use warnings; use strict; <DATA>; # Skip header. my %tree; while (<DATA>) { chomp; my ($child, $parent, $relation) = split /,\t/; $relation =~ s/,$//; $tree{$child}{parent} = [$parent, $relation]; } for my $node (keys %tree) { my @path = $node; my @relations = ($tree{$node}{parent}[1]); my $parent = $node; while ($parent = $tree{$parent}{parent}[0]) { push @path, $parent; push @relations, $tree{$parent}{parent}[1]; } print "$node:\t"; print join '<-', @path; print " = ", join '.', reverse grep defined, @relations; print "\n"; } __DATA__ child, Parent, relation M10, Q, P, M143, M10, P, M406, M143, PL, M407, M143, PL, M420, M143, E, M421, M143, E,

    Output:

    M143: M143<-M10<-Q = P.P M420: M420<-M143<-M10<-Q = P.P.E M407: M407<-M143<-M10<-Q = P.P.PL M10: M10<-Q = P M421: M421<-M143<-M10<-Q = P.P.E M406: M406<-M143<-M10<-Q = P.P.PL
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
      Dear choroba,

      Suppose P.P belongs to group 'A', P.P.E belongs to group 'B',,So Id like to have their group names output against them also.

      M143: M143<-M10<-Q = P.P, A M420: M420<-M143<-M10<-Q = P.P.E, B M407: M407<-M143<-M10<-Q = P.P.PL, C M10: M10<-Q = P, D M421: M421<-M143<-M10<-Q = P.P.E, B M406: M406<-M143<-M10<-Q = P.P.PL, C
      Of course number of groups will be hardly 4 or 5. How can I do it ?
        Create a hash that maps the method strings to groups.
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: constructing a tree from csv file
by hdb (Monsignor) on Nov 01, 2013 at 10:11 UTC

    The tree I am constructing is based on code references calling the parents subs for its parents and relations:

    use strict; use warnings; my %data; while(<DATA>){ my( $child, $parent, $relation ) = split /,\s*/; $data{$child} = { parent => sub { $child.(exists $data{$parent}?'<-' +.$data{$parent}{parent}->():"<-$parent") }, relation => sub { (exists $data{$parent}?$data{$pa +rent}{relation}->().'.':'' ).$relation } }; } print $_->{parent}->()." = ".$_->{relation}->()."\n" for values %data; __DATA__ M10, Q, P, M143, M10, P, M406, M143, PL, M407, M143, PL, M420, M143, E, M421, M143, E,
Re: constructing a tree from csv file
by oiskuu (Hermit) on Nov 01, 2013 at 12:35 UTC
    Ahem, this isn't going to win style points (I think), but here goes:
    #! /usr/bin/perl -l my %T = map {$_->[0] => $_} map {[split /,\s*/]} <DATA>; sub xz4 { ["$_[0]<-$_[2]", "$_[3].$_[1]"] } sub xln { my @r = @{@T{@_} // return (@_, '')}; @{ $r[3] //= xz4 @r[0,2], xln($r[1]) }; } print grep {s/\./= /} join " ", xln($_) for keys %T; __DATA__ M10, Q, P, M143, M10, P, M406, M143, PL, M407, M143, PL, M420, M143, E, M421, M143, E,
      oiskuu if I run your code on this data:-
      __DATA__ M7, Q, P, M7, M28, E, M28, M6, E, M6, Q, Pl,
      I get this
      M7<-M28<-M6<-Q = Pl.E.E M6<-Q = Pl M28<-M6<-Q = Pl.E
      Whereas I expect
      M7<-Q = P M28<-M6<-Q = Pl.E M6<-Q = Pl
      That is I expect only the shortest path from child to parent if it is there.
        It appears your problem has substantially altered. Please post a new question with all specifics.
        Are the relations fixed to those three? Do they have a cost metric? If multiple shortest paths exist?