NAME

Data::Iterator::Hierarchical - Iterate hierarchically over tabular data

VERSION

Version 0.03

SYNOPSIS

      my $sth = $db->prepare(<<SQL);
        SELECT agent, co, co_name, sound
        FROM some_view_containing_left_joins 
        ORDER BY agent, co, sound
      SQL

      $sth->execute;

      my $it = hierarchical_iterator($sth);

      while( my($agent) = $it->(my $it_co, 1)) {
	print "agent=$agent\n";
	while( my ($co,$co_name) = $it_co->(my $it_sound, 2) ) {
	  print "  co=$co, co_name=$co_name\n";
	  while( my($sound) = $it_sound->() ) {
	    print "    sound=$sound\n";   
	  }
	}
      }

DESCRIPTION

This module allows nested loops to iterate in the natural way over a sorted rowset as would typically be returned from an SQL database query that is the result of naturally left joining several tables.

In the example from the synopsis we want an interator that loops over agent. Within that we want another interator to loop over country (code and name). Finally within that we want to loop over sound.

And mostly that's all there is to say the iterator should just "Do What I Mean" (DWIM).

input

 agent   |  co     | co_name  | sound
=========|=========|==========|========
  X      |  B      | Belgum   | fizz
  X      |  D      | Germany  | bang
  X      |  D      | Germany  | pow
  X      |  D      | Germany  | zap
  Y      |  NULL   | NULL     | NULL
  Z      |  B      | Belgum   | NULL
  Z      |  E      | Spain    | bar 
  Z      |  E      | Spain    | bar 
  Z      |  I      | Italy    | foo

output

agent=X
  co=B, co_name=Belgum
    sound=fizz
  co=D, co_name=Germany
    sound=bang
    sound=pow
    sound=zap
agent=Y
agent=Z
  co=B, co_name=Belgum
  co=E, co_name=Spain
    sound=bar
    sound=bar
  co=I, co_name=Italy
    sound=foo

EXPORT

hierarchical_iterator

FUNCTIONS

hierarchical_iterator($rowset_source)

A factory for iterator functions. Takes a rowset source as an argument and returns an interator function.

The input rowset is cannonically presented as iterator function that is to be called with no arguments in a list context and is expected to return the next row from the set as a list. When the input is exhasted the iterator is expected to return an empty list.

For convienience the data source can also be specified simply as \@array in which case the interator sub { shift @array } is assumed. Finally, if the data source is specified as anything other than an ARRAY or CODE reference then it is assumed to be an object that provides a fetchrow_array() method (such as a DBI handle).

$iterator->($inner_iterator,$want)

The ineresting function from this module is, of course, the iterator function returned by the iterator factory. This iterator when called in a list context without arguments simply returns the next a row of data, or an empty list to denote exhaustion. It is an error to call the iterator in a non-LIST context. Input rows that consist entirely of undef()s are skipped.

So if the iterator returned by the hierarchical_iterator() factory is called without arguments it behaves pretty much the same as the iterator that was supplied as the input except for the skipping of null rows. The interesting stuff starts happening when you pass arguments to the iterator function.

The second argument instructs the interator to return only a limited number of leading columns to from each row. The first argument is used to return an inner Data::Iterator::Hierarchical iterator that will iterate over only the successive rows of the input until the leading colums change and return only the remaining columns.

my ($col1,$col2) = $iterator->(my $inner_iterator,2);

The two arguments are specified in a seemingly illogical order because the second argument becomes optional if the Want module is installed and the iterator is used in a simple list assignment (as above). In this case the number of columns can be inferred from the left hand side of the assignment.

If the interator returned in $inner_iterator is not used (or even not used to exhastion) then the next invocation of $iterator will discard all input rows until there is a different pair of values in the first two columns.

BUGS AND CAVEATS

In versions of Perl before 5.10 this module leaks closures as a consequnce of a bug in Perl's handling of reference counts. Consequnently the input iterator will not get released on these versions of Perl unless it is read to exhastion.

To do: (need stuff in here about nulls, non-rectangular input, repeated rows, changing the number of columns half way etc.)

AUTHOR

Brian McCauley, <nobull at cpan.org>

BUGS

Please report any bugs or feature requests to bug-data-iterator-hierarchical at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Data-Iterator::Hierarchical. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc Data::Iterator::Hierarchical

You can also look for information at:

ACKNOWLEDGEMENTS

COPYRIGHT & LICENSE

Copyright 2008 Brian McCauley, all rights reserved.

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