The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Data::TreeDraw - Graphical representation of nested data structures.

VERSION

This document describes Data::TreeDraw version 0.0.1

DESCRIPTION

While there are a number of great programs out there for Dumping and visualising heavily nested data structures these can often be over-whelming and hard to read. Additionally, often in order to access a particular data entry you often have to back-trace over a Dumped structure to find the specific code appropriate to dereference that particular value. This module aims to address these issues and several others by providing number of different features with a very simple interface that is particularly aimed at helping to visualise data-rich heavily-nested structures - see OVERVIEW.

This module exports a single sub-routine call draw. Simply call this routine with the data structure you wish to print along with a HASH reference of any options you wish to pass - see OPTIONS and OVERVIEW.

This module was written by me, for me and so internally may be a bit esoteric. If there is significant interest I will improve and expand it.

SYNOPSIS

Create suitable structure.

    # Create a Code Reference.
    my $c_ref = sub { print q{blah}; $_->[0] };
    # Create a heavily nested set of references.
    my $r_ref = \\\\\[q{pink},321];
    # Create an object of type HASH;
    my $pca->{g} = 4;
    bless $pca, q{Some::Class}; 
    # Create a GLOB.
    *f = *g;

    # Create a nested data structure with various typed of nested data e.g. defined and undefined SCALAR, ARRAY, HASH and REFs
    my $a = [ q{hi}, q{there}, [ q{i}, q{am} ], 1, { r => \\q{} }, { r => { y => 3 }, t => [ { r => *g } ] }, $c_ref, *f, $r_ref, q{nine}, [ [ 3,q{----3-----}, 3 ], 
    [3, 3, 3, 3, ], [ 3, 3, 3, ] ], 1, \\\{ r => \[] }, \\\q{foo}, { g => 2, t => 2, y => q{------2-------}, r => [3], step => { the => q{blah} }, o => [ [4] 
    ] }, [ [ [q{--4--}]] , [ [ 4, 4, q{-4-}, q{--4--} ] ], 2, [3] ], \\\q{}, 1, [ [3 , 3], 2, $pca] , 1, { g => [ 3, 3, q{blah}, 3, 3, 3 ] } ,[ [ 3 ] ], $a, undef ];
    
    # Create cyclic references.
    my $b = [$a]; 
    my $c = [$b];
    $a->[2][3] = $c;
    $a->[2][2] = $a;
    # Make our nested data structure an object
    bless $a, q{Other::Class};

Use module and call draw routine on structure.

    use Data::TreeDraw;

    draw($a);

Prints:

    Method called from Package 'main' on Blessed Object of type 'ARRAY' and Class 'Other::Class'.

    ARRAY REFERENCE (0)
      |  
      |__SCALAR = 'hi' (1)  [ '->[0]' ]
      |  
      |__SCALAR = 'there' (1)  [ '->[1]' ]
      |  
      |__ARRAY REFERENCE (1) [ '->[2]' ]
      |    |  
      |    |__SCALAR = 'i' (2)  [ '->[2][0]' ]
      |    |  
      |    |__SCALAR = 'am' (2)  [ '->[2][1]' ]
      |    |  
      |    |__CYCLIC REFERENCE (2) [ '->[2][2]' ]
      |    |  
      |    |__ARRAY REFERENCE (2) [ '->[2][3]' ]
      |         |  
      |         |__ARRAY REFERENCE (3) [ '->[2][3][0]' ]
      |              |  
      |              |__CYCLIC REFERENCE (4) [ '->[2][3][0][0]' ]
      |  
      |__SCALAR = '1' (1)  [ '->[3]' ]
      |  
      |__HASH REFERENCE (1) [ '->[4]' ]
      |    |  
      |    |__'r'=>REFERENCE-TO-REFERENCE (2)
      |         |  
      |         |__SCALAR REFERENCE (3)
      |              |  
      |              |__SCALAR = '' [EMPTY STRING] (4) 
      |  
      |__HASH REFERENCE (1) [ '->[5]' ]
      |    |  
      |    |__'r'=>HASH REFERENCE (2) [ '->[5]{r}' ]
      |    |    |  
      |    |    |__'y'=>SCALAR = '3' (3)  [ '->[5]{r}{y}' ]
      |    |  
      |    |__'t'=>ARRAY REFERENCE (2) [ '->[5]{t}' ]
      |         |  
      |         |__HASH REFERENCE (3) [ '->[5]{t}[0]' ]
      |              |  
      |              |__'r'=>GLOB = '*main::g' (4)  [ '->[5]{t}[0]{r}' ]
      |  

      etc.

OVERVIEW

Tree Structure

All structures are displayed as a "clear" Tree-structure branching from a single root.

     ARRAY REFERENCE (0)
     |  
     |__SCALAR = 'hi' (1)  [ '->[0]' ]
     |  
     |__SCALAR = 'there' (1)  [ '->[1]' ]
     |  
     |__ARRAY REFERENCE (1) [ '->[2]' ]
     |    |  
     |    |__SCALAR = 'i' (2)  [ '->[2][0]' ]
     |    |  
     |    |__SCALAR = 'am' (2)  [ '->[2][1]' ]
     |    |  
     |    |__CYCLIC REFERENCE (2) [ '->[2][2]' ]
     |    |  
     |    |__ARRAY REFERENCE (2) [ '->[2][3]' ]
     |         |  
     |         |__ARRAY REFERENCE (3) [ '->[2][3][0]' ]
    
     etc.

Notation

The notation option (defaults to on - with "1") results in the printing along side (in square-brackets) the specific REFERENCE or SCALAR value of the particular arrow-notation required to access/dereference that REFERENCE or SCALAR value e.g. "[ '->[4]{hash_key}[12]' ]" next to an ARRAY reference means that to dereference that particular value within the passed structure simply append "->[4]{hash_key}[12] " to the originally passed reference.

     e.g. To access the above ARRAY reference printed as: "ARRAY REFERENCE (2) [ '->[2][3][0]' ]" 
     Simply use: $original_data_passed->[2][3][0];

Spacing

For a more compressed version of the print disable the spaces options by setting it to "0" (this option is enabled by default). The above Tree is printed as:

     ARRAY REFERENCE (0)
       |__SCALAR = 'hi' (1)  [ '->[0]' ]
       |__SCALAR = 'there' (1)  [ '->[1]' ]
       |__ARRAY REFERENCE (1) [ '->[2]' ]
       |    |__SCALAR = 'i' (2)  [ '->[2][0]' ]
       |    |__SCALAR = 'am' (2)  [ '->[2][1]' ]
       |    |__CYCLIC REFERENCE (2) [ '->[2][2]' ]
       |    |__ARRAY REFERENCE (2) [ '->[2][3]' ]
       |         |__ARRAY REFERENCE (3) [ '->[2][3][0]' ]
       |              |__CYCLIC REFERENCE (4) [ '->[2][3][0][0]' ]

       etc.

Indentation Level

The nesting/indentation level of ALL structures is printed along-side of the REFERENCE/SCALAR value in parenthesis:

     e.g. SCALAR = 'some_value' (nesting_level/e.g.4) 

EMPTY STRINGS

Any SCALAR value containing an empty string is printed as that e.g. '', but to ease distinguishing it from ' ' it additionally prints [EMPTY STRING] along-side.

     e.g. SCALAR = '' [EMPTY STRING]

Long Arrays

With data-rich structures arrays may often have many elements. In such cases printing each SCALAR value within the array on a separate line makes reading the structure difficult:

     ARRAY REFERENCE (2) [ '->[20]{g}' ]
       |  
       |__SCALAR = 'val1' (3)  [ '->[20]{g}[0]' ]
       |  
       |__SCALAR = 'val2' (3)  [ '->[20]{g}[1]' ]
       |  
       |__SCALAR = 'val3' (3)  [ '->[20]{g}[2]' ]
       |  
       |__SCALAR = 'val4' (3)  [ '->[20]{g}[3]' ]

       etc.

The long_array option (defaults to off - "0") over-rides this behaviour and instead arrays consisting of "just" SCALAR values are printed on a single-line. With this setting relatively short arrays are printed in full on a single line along with their length:

     ARRAY REFERENCE (3) ---LONG_LIST_OF_SCALARS--- [ length = 4 ]: val1, val2, val3, val4 [ '->[20]{g}' 

Longer arrays are printed in a similar fashion except that only the length and first 3 elements are printed (just to indicate the nature of the values stored by the array).

     ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 4 ] e.g. 0..2:  val1, val2, val3 [ '->[20]{g}' ]

You can switch the length of array that triggers these two behaviours using the array_length option (defaults to 3). See OPTIONS for further info.

Lists-of-Lists.

In cases of Lists-of-lists the readability may suffer further - especially as these structures often correspond to 2-dim tables. Thus in cases of ARRAYS consisting uniquely of ARRAYS of SCALARS:

     |__ARRAY REFERENCE (2) [ '->[10][0]' ]
     |    |  
     |    |__SCALAR = '2' (3)  [ '->[10][0][0]' ]
     |    |  
     |    |__SCALAR = '3' (3)  [ '->[10][0][1]' ]
     |    |  
     |    |__SCALAR = '3' (3)  [ '->[10][0][2]' ]
     |  
     |__ARRAY REFERENCE (2) [ '->[10][1]' ]
     |    |  
     |    |__SCALAR = '3' (3)  [ '->[10][1][0]' ]
     |    |  
     |    |__SCALAR = '3' (3)  [ '->[10][1][1]' ]
     |    |  
     |    |__SCALAR = '3' (3)  [ '->[10][1][2]' ]
     |    |  
     |    |__SCALAR = '3' (3)  [ '->[10][1][3]' ]
     |  
     |__ARRAY REFERENCE (2) [ '->[10][2]' ]
          |  
          |__SCALAR = '3' (3)  [ '->[10][2][0]' ]
          |  
          |__SCALAR = '3' (3)  [ '->[10][2][1]' ]
          |  
          |__SCALAR = '3' (3)  [ '->[10][2][2]' ]

The lol option when set to "1" will replace these structures with (this option defaults to "0"):

    ARRAY REFERENCE (1) ---LIST OF LISTS--- [ rows = 3 and longest nested list length = 4 ] [ '->[10]' ]

You may be interested in the particular values within these structures. In this case lol set to "2" creates a temporary break in the tree-structure with a table of the values and their access values and an extension of their root:

     ARRAY REFERENCE (1) ---LIST OF LISTS--- [ rows = 3 and longest nested list length = 4 ] [ '->[10]' ]
       |  
      --- 

 .----------+-------+-------+-------+-------.
 |          | ..[0] | ..[1] | ..[2] | ..[3] |
 +----------+-------+-------+-------+-------+
 | ..[0]..  | '3'   | '3'   | '3'   | ---   |
 | ..[1]..  | '3'   | '3'   | '3'   | '3'   |
 | ..[2]..  | '3'   | '3'   | '3'   | ---   |
 '----------+-------+-------+-------+-------'

      --- 
       |  

This option may be used with the long_arrays option in which case the lol option takes precedence and above structure would be printed as above instead of:

     |__ARRAY REFERENCE (1) [ '->[10]' ]
        |  
        |__ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 3 ]: 3, 3, 3 [ '->[10][0]' ]
        |  
        |__ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 4 ]: 3, 3, 3, 3 [ '->[10][1]' ]
        |  
        |__ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 3 ]: 3, 3, 3 [ '->[10][2]' ]

HASH keys.

As HASHES are simply unordered LISTs using a look up key HASH references are displayed just ARRAY references only with the hash key appended e.g.

     |__HASH REFERENCE (1) [ '->[20]' ]
          |  
          |__'hash_key'=>ARRAY REFERENCE (2) [ '->[20]{g}' ]
               |  
               |__SCALAR = '3' (3)  [ '->[20]{g}[0]' ]

HASH key lookup

You may be interested in just the values of a particular HASH entry. In this case using the hash_key option you can start Tree printing from that particular HASH key. See OPTIONS for usage.

    HASH key 'given_key' found and indentation level '5':

                    |__'given_key'=>REFERENCE-TO-REFERENCE (5)
                         |    |  
                         |__UNDEFINED ARRAY REFERENCE (6) 
      
                    etc.

    HASH key 'given_key' found 2 times in nested data structure.

Minimum printing depth

You may not be interested in values near the root of the structure. In which case you can set the min_depth option (defaults to 0).

     Starting print at depth 2.
 
     |    |__SCALAR = 'i' (2)  [ '->[2][0]' ]
     |    |  
     |    |__SCALAR = 'am' (2)  [ '->[2][1]' ]
     |    |  
     |    |__CYCLIC REFERENCE (2) [ '->[2][2]' ]
     |    |  
     |    |__ARRAY REFERENCE (2) [ '->[2][3]' ]
     |         |  
     |         |__ARRAY REFERENCE (3) [ '->[2][3][0]' ]
     |              |  
     |              |__CYCLIC REFERENCE (4) [ '->[2][3][0][0]' ]
     |  

     Indent decrementing to '1' below min_depth level of '2'

     |    |__'r'=>REFERENCE-TO-REFERENCE (2)
     |         |  
     |         |__SCALAR REFERENCE (3)
     |              |  
     |              |__SCALAR = '' [EMPTY STRING] (4) 
     |  

Maximum printing depth

If you do not wish to view deeply nested structures you can set the max_depth option (defaults to 10):

     ARRAY REFERENCE (0)
     |  
     |__SCALAR = 'hi' (1)  [ '->[0]' ]
     |  
     |__SCALAR = 'there' (1)  [ '->[1]' ]
     |  
     |__ARRAY REFERENCE (1) [ '->[2]' ]
     |    |  
     |    |__SCALAR EXCEEDS MAX NESTING DEPTH (2)
     |    |  
     |    |__SCALAR EXCEEDS MAX NESTING DEPTH (2)
     |    |  

     etc.

Object recursion.

In cases where an object reference is pointed within the structure its class will be printed:

     |__BLESSED OBJECT BELONGING TO CLASS: Statistics::PCA (2)  [ '->[18][2]' ]

However, you may be wish to continue the recursion into the object. This can be done by setting the unwrap_object option to "1" (defaults to "0"):

     |  
     |__BLESSED OBJECT BELONGING TO CLASS: Statistics::PCA (3) ---RECURSING-INTO-OBJECT--- 
          |  
          |  
          |__HASH REFERENCE (3)
               |  

               etc.

Note: while the structure is indented further - the actual indentation level in parenthesis does not change - this is just aids the identification of the type of data-type of the object within the structure. Also as yet, the notation option is not supported with the object_unwrap option.

Method introspection for objects

You may additionally wish to introspect either the root structure or lower-level objects for their methods. This module can use the introspection facility of Class::MOP and print a formated table by setting the object_methods option to "1" (as with lol this temporarily breaks the tree structure):

     |__BLESSED OBJECT BELONGING TO CLASS: Statistics::PCA (3) ---RECURSING-INTO-OBJECT--- 
          |  
         --- 

 .-----------------------------------------------------.
 | Methods                                             |
 +-----------------------------------------------------+
 | Statistics::PCA::_deep_copy_references              |
 | Statistics::PCA::print_eigenvectors                 |
 | Statistics::PCA::_calculate_eigens_cephes           |
 | ...                                                 |
 '-----------------------------------------------------'

         --- 
          |  
          |__HASH REFERENCE (3)
               |  

               etc.

OPTIONS

All options are passed by hash reference:

    draw($data, {max_depth => 3, unwrap_objects => 1, object_methods => 1} );

array_limit

    Name:           array_limit
    Description:    Used in conjunction with long_array option. Specifies the cutoff point for printing an entire array of SCALARS and just first few example elements.
    Usage:          draw($data, {array_limit => 6});
    Values:         3-10.
    Default:        5.    

hash_key

    Name:           hash_key
    Description:    Allows printing of just those elements within a HASH of interest within a structure.
    Usage:          draw($data, {hash_key => q{a_key_name});
    Values:         String.     
    Default:        undef.

lol

    Name:           lol
    Description:    This option suppresses long-outputs given from Lists-of-Lists - see OVERVIEW.
    Usage:          draw($data, {lol => 2});
    Values:         0, 1, 2.
    Default:        0.

long_array

    Name:           long_array
    Description:    This option suppresses long-output from long arrays of SCALARS - see OVERVIEW.
    Usage:          draw($data, {long_array => 2});
    Values:         0, 1.
    Default:        0.

max_depth

    Name:           max_depth
    Description:    Specifies the maximum indentation/nesting depth to proceed to - see OVERVIEW.
    Usage:          draw($data, {max_depth => 6});
    Values:         0-10.
    Default:        10.

max_methods

    Name:           max_methods 
    Description:    Used in conjunction with the object_methods option. Specifies the maximum number of object methods to print.
    Usage:          draw($data, {max_methods => 6});
    Values:         1-100.
    Default:        50.    

min_depth

    Name:           min_depth
    Description:    Specifies the minimum indentation depth to start printing at - see OVERVIEW.
    Usage:          draw($data, {array_limit => 6});
    Values:         0-9.
    Default:        0.

notation

    Name:           notation
    Description:    This option enables or disables the automatic arrow "->" notation printing specifying how to dereference a particular entity within the structure.
    Usage:          draw($data, {notation => 0});
    Values:         0, 1.
    Default:        1.

object_methods

    Name:           object_methods
    Description:    Turns on object method introspection using the Class::MOP module.
    Usage:          draw($data, {object_methods => 1});
    Values:         0, 1.
    Default:        0.

spaces

    Name:           spaces
    Description:    This option enables and disables the printing of extra "branch" lines in the Tree structure for easier visualisation.
    Usage:          draw($data, {spaces => 0});
    Values:         0, 1.
    Default:        1.

unwrap_objects

    Name:           unwrap_objects
    Description:    This option causes the program to recurse into objects that fall within a data structure.
    Usage:          draw($data, {unwrap_objects => 1});
    Values:         0, 1.
    Default:        1.

borders

    Name:           borders - This option is not yet fully implemented.
    Description:
    Usage:          Disabled atm as not yet fully implemented.
    Values:         "0", "1".
    Default:        "0" (off).    

DEPENDENCIES

Scalar::Util => "1.22", Class::MOP => "0.95", Text::SimpleTable => "2.0", Carp => "1.08",

AUTHOR

Daniel S. T. Hughes <dsth@cantab.net>

SEE ASLO

Data::Dumper, Data::TreeDumper.

LICENCE AND COPYRIGHT

Copyright (c) 2009, Daniel S. T. Hughes <dsth@cantab.net>. All rights reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.

DISCLAIMER OF WARRANTY

because this software is licensed free of charge, there is no warranty for the software, to the extent permitted by applicable law. Except when otherwise stated in writing the copyright holders and/or other parties provide the software "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the software is with you. Should the software prove defective, you assume the cost of all necessary servicing, repair, or correction.

In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who may modify and/or redistribute the software as permitted by the above licence, be liable to you for damages, including any general, special, incidental, or consequential damages arising out of the use or inability to use the software (including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or third parties or a failure of the software to operate with any other software), even if such holder or other party has been advised of the possibility of such damages.