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

I have a self-referencing list which needs to be converted into a hash tree structure. When I used Sort::Tree to solve this, from its behaviour, I thought it would only parse the data structure to one level +1, leaving some deeper-level-kids on the top level.

When I began to develop a workaround, I also elaborated the data structure and found out that Sort::Tree actually does build multi-level trees - it only forgets about the last child and leaves it on top level.

I think this is a bug, but it might be I am using the module wrongly. So the test script here is to illustrate the strange behaviour and it has the routines I used to remedy the misbehaviour before I found the "bug". RFC on both.
#!/usr/bin/perl use strict; use warnings; use Sort::Tree; use YAML::XS; use Data::Dumper; ## load structure my $yaml = YAML::XS::Load( yaml() ); ## convert structure to array my @list; foreach my $key (reverse sort keys %$yaml){ my $item = $$yaml{$key}; push(@list, $item); } ## compare the output of these: my @tree = list_to_tree(\@list, 'id', 'parent'); # my @tree = list_to_multileveltree(\@list, 'id', 'parent'); print Dumper(\@tree); sub list_to_multileveltree { my ($arrayref,$id,$parent) = @_; my @tree = list_to_tree($arrayref, $id, $parent); # while(has_kids(\@list)){ for my $index (0 .. $#tree){ my $task = $tree[$index]; if($task->{$parent}){ Sort::Tree::traverse(\@tree, method => \&appendKid +s, removeId => $task->{$id +}, removeIndex => $index, parentId => $task->{$pa +rent}, treeref => \@tree, ); } } # } return @tree; } sub appendKids { my %details = @_; if($details{item}->{id} == $details{parentId}){ $details{item}->{appendKids} = 'was here' . " ## $deta +ils{removeId} $details{removeIndex} $details{parentId}"; push( @{ $details{item}->{kids} }, ${ $details{treeref} }[$details{removeIndex}] ); splice(@{ $details{treeref} }, $details{removeIndex}, +1); } return 0; # continue processing tree } # check if the tree has kids left on top level (unused) sub has_kids { my $ref = shift; my $has_kids; foreach my $key (keys %$ref){ my $task = $$ref{$key}; if($task->{parent}){ $has_kids = 1; } } return $has_kids; } sub yaml { return " --- '1317074400': id: 1317074400 '1320678979': id: 1320678979 body: Nest Top '1320678979.5': id: 1320678979.5 parent: 1320678979 body: Nest 1 '1320678979.01': id: 1320678979.01 parent: 1320678979 body: Nest 1.1 '1320678979.1': id: 1320678979.1 body: Nest 2 '1320678979.2': id: 1320678979.2 parent: 1320678979.1 body: Nest G '1320678979.3': id: 1320678979.3 parent: 1320678979.2 body: Nest 3.1 '1320678979.4': id: 1320678979.4 parent: 1320678979.2 body: Nest 3.2 '1320678981': id: 1320678981 body: Nest 0 '1320678982': id: 1320678982 body: Nest 1 parent: 1320678981 '1320678983': id: 1320678983 parent: 1320678982 body: Nest 2 '1320678984': id: 1320678984 parent: 1320678983 body: Nest 4 '1320678985': id: 1320678985 parent: 1320678984 body: Nest 5 "; }

Replies are listed 'Best First'.
Re: Is this a bug in Sort::Tree?
by toolic (Bishop) on Nov 15, 2011 at 16:26 UTC
    I don't know if this is a bug because I have no familiarity with the module. But here are some things to consider.

    The CPAN distribution for Sort::Tree has no real tests. This means that there is no proof on CPAN that this module works in any mode of operation.

    You could locally hack the module's source code and set the following variable to 1. It might give you more information as you debug your problem:

    use constant DEBUGGING => 0;

    I can not find any code snippets showing uses of this module. I looked on CPAN, where module authors sometimes add code in "examples" directories, and I ran a Super Search here at the Monastery. The CPAN "t" test directory is usually a good source of example uses, but in this case, there are no real tests. The module's POD seems to be the only source of example code.

    If you decide to submit a bug report, I recommend getting rid of the YAML dependency. Try to show expected vs. actual output. Better yet, create an actual test (using Test::More) which fails and include it with the bug report.