File Coverage

blib/lib/Data/Presenter.pm
Criterion Covered Total %
statement 591 591 100.0
branch 155 180 86.1
condition 4 6 66.6
subroutine 68 68 100.0
pod 21 21 100.0
total 839 866 96.8


line stmt bran cond sub pod time code
1             package Data::Presenter;
2             #$Id: Presenter.pm 1218 2008-02-10 00:11:59Z jimk $
3             $VERSION = 1.03; # 02-10-2008
4 11     11   24037 use strict;
  11         28  
  11         523  
5 11     11   68 use warnings;
  11         19  
  11         444  
6 11     11   15262 use List::Compare::Functional qw( is_LsubsetR );
  11         179126  
  11         10740  
7 11     11   130 use Carp;
  11         24  
  11         668  
8 11     11   7639 use Data::Dumper;
  11         62707  
  11         31719  
9              
10             ############################## Package Variables ##############################
11              
12             our %fp = ();
13             our %fieldlabels = ();
14             our %reserved = map {$_ => 1} qw( fields parameters index options );
15              
16             our %gt_lt_ops = map {$_ => 1} (
17             q{<}, q{lt}, q{>}, q{gt}, q{<=}, q{le}, q{>=}, q{ge},
18             );
19              
20             my %eq = map {$_, 'eq'} (
21             q{eq}, q{equals}, q{is}, q{is equal to}, q{is a member of}, q{is part of}, q{=}, q{==},
22             );
23             my %ne = map {$_, 'ne'} (
24             q{ne}, q{is not}, q{is not equal to}, q{is not a member of}, q{is not part of}, q{is less than or greater than}, q{is less than or more than}, q{is greater than or less than}, q{is more than or less than}, q{does not equal}, q{not}, q{not equal to}, q{not equals}, q{!=}, q{! =}, q{!==}, q{! ==}, q{<>},
25             );
26             my %lt = map {$_, 'lt'} (
27             q{<}, q{lt}, q{is less than}, q{is fewer than}, q{before},
28             );
29             my %gt = map {$_, 'gt'} (
30             q{>}, q{gt}, q{is more than}, q{is greater than}, q{after},
31             );
32             my %le = map {$_, 'le'} (
33             q{<=}, q{le}, q{is less than or equal to}, q{is fewer than or equal to}, q{on or before}, q{before or on},
34             );
35             my %ge = map {$_, 'ge'} (
36             q{>=}, q{ge}, q{is more than or equal to}, q{is greater than or equal to}, q{on or after}, q{after or on},
37             );
38              
39             our %all_relations;
40             foreach my $rel ( \%eq, \%ne, \%lt, \%gt, \%le, \%ge) {
41             foreach my $key (keys %{$rel}) {
42             $all_relations{$key} = $rel->{$key};
43             }
44             }
45              
46             our %sortclass = (
47             eq => { a => q{eq}, s => q{eq}, n => q{==} },
48             ne => { a => q{ne}, s => q{ne}, n => q{!=} },
49             lt => { a => q{lt}, s => q{lt}, n => q{<} },
50             gt => { a => q{gt}, s => q{gt}, n => q{>} },
51             le => { a => q{le}, s => q{le}, n => q{<=} },
52             ge => { a => q{ge}, s => q{ge}, n => q{>=} },
53             );
54              
55             ################################# Constructor #################################
56              
57             sub new {
58 148     148 1 664887 my ($inputs, $class, $source, $fieldsref, $paramsref,
59             $index, $self, $dataref, $datapoints, @defective_records);
60 148         329 $inputs = scalar(@_);
61              
62 148 100       591 if ($inputs == 5) {
    100          
63             # regular Data::Presenter object immediately validates data
64 138         611 ($class, $source, $fieldsref, $paramsref, $index) = @_;
65 138         577 _validate_fields($fieldsref);
66 137         1957 _validate_parameters($fieldsref, $paramsref);
67 134         554 _validate_index($fieldsref, $index);
68             } elsif ($inputs == 2) {
69             # Data::Presenter::Combo object: data already validated
70 9         29 ($class, $source) = @_;
71             } else {
72 1         4 my ($package) = caller;
73 1         349 croak 'Wrong number of inputs to ', $package, '::new', "$!";
74              
75             }
76              
77             # bless a ref to an empty hash into the invoking class
78             # which is somewhere below this one in the hierarchy
79 141         880 $self = bless( {}, $class );
80              
81             # prepare the database by using &_init from package somewhere below
82             # this one
83 141 100       527 if ($inputs == 5) {
84 132         1766 $dataref = $self->_init($source, $fieldsref, $paramsref,
85             $index, \%reserved);
86             } else {
87 9         113 $dataref = $self->_init($source);
88             }
89              
90             # carp if, other than reserved words, the object has 0 records
91             # croak if, other than reserved words, the object has records with
92             # undefined elements
93 137         111195 foreach my $rec (keys %$dataref) {
94 2229 100       5829 unless ($reserved{$rec}) {
95 1813         2076 $datapoints++;
96 1813         1964 my $undefcount = 0;
97 1813         2203 foreach my $el ( @{ $dataref->{$rec} } ) {
  1813         3235  
98 13144 100       51844 $undefcount++ if not defined $el;
99             }
100 1813 100       4709 push @defective_records, $rec if $undefcount;
101             }
102             }
103 137 100       805 carp "Object initialized, $class, contains 0 data elements: $!"
104             unless ($datapoints);
105 137 100       638 croak "Records @defective_records have undefined elements;\n consider revising initialization subroutine: $!"
106             if @defective_records;
107              
108             # prepare 2 hashes which will be needed in selecting rows and
109             # sorting columns
110 136         5345 _make_labels_params(
111 136         200 \@{${$dataref}{'fields'}}, \@{${$dataref}{'parameters'}});
  136         205  
  136         175  
  136         488  
112              
113             # initialize the object from the prepared values (Damian, p. 98)
114 136         1676 %$self = %$dataref;
115 136         1204 return $self;
116             }
117              
118             ################################################################################
119             ##### Subroutines called from with &new (constructor)
120             ################################################################################
121              
122             sub _validate_fields {
123 138     138   234 my $fieldsref = shift;
124 138         395 my %seen = ();
125 138         352 foreach my $field (@$fieldsref) {
126             # Note: Assuming that _init() has been written correctly in the
127             # Data::Presenter::subclass in which the object is created, the
128             # 'croak' branch below will never be reached.
129 955 100       2983 $seen{$field} ? croak "$field is a duplicated field in \@fields: $!"
130             : $seen{$field}++;
131             } # Confirmed: there exist no duplicated fields in @fields.
132             }
133              
134             sub _validate_parameters {
135 137     137   797 my ($fieldsref, $paramsref) = @_;
136 137         567 my @fields = @$fieldsref;
137 137         1043 my %parameters = %$paramsref;
138 137         322 my ($i, $badvalues);
139 137         541 for ($i = 0; $i < scalar(@fields); $i++) {
140 952         2733 my @temp = @{$parameters{$fields[$i]}};
  952         5198  
141 952 100 66     11519 $badvalues .= ' ' . $fields[$i] . "\n"
      66        
142             if ($temp[0] !~ /^\d+$/ # 1st element must be numeric
143             ||
144             $temp[1] !~ /^[UD]$/i # 2nd element must be U or D (lc or uc)
145             ||
146             $temp[2] !~ /^[ans]$/i # 3rd element must be a, n or s
147             );
148             }
149 137 100       1175 croak "Need corrected values for these keys:\n$badvalues:$!" if ($badvalues);
150             }
151              
152             sub _validate_index {
153 134     134   242 my ($fieldsref, $index) = @_;
154 134         358 my @fields = @$fieldsref;
155 134 100       1021 croak "\$index must be a numeral: $!"
156             unless ($index =~ /^\d+$/);
157 133 100       828 croak "\$index must be < number of elements in \@fields: $!"
158             unless $index <= $#fields;
159             }
160              
161             sub _make_labels_params {
162 136     136   723 my ($fieldsref, $paramsref) = @_;
163 136         440 my @fields = @$fieldsref;
164 136         385 my @aryparams = @$paramsref;
165 136         851 %fp = ();
166 136         314 my %temp = ();
167 136         453 for (my $i = 0; $i < scalar(@fields); $i++) {
168 966         980 $fp{$fields[$i]} = [@{$aryparams[$i]}];
  966         3017  
169 966         3498 $temp{$fields[$i]} = $i;
170             }
171 136         1482 %fieldlabels = %temp;
172             }
173              
174             ################################################################################
175             ##### Subroutines to get information on the Data::Presenter object
176             ################################################################################
177              
178             sub get_data_count {
179 110     110 1 29068 my $self = shift;
180 110         285 _count_engine($self);
181             }
182              
183             sub print_data_count {
184 12     12 1 99099 my $self = shift;
185 12         80 print 'Current data count: ', _count_engine($self), "\n";
186             }
187              
188             sub _count_engine {
189 122     122   194 my $self = shift;
190 122         834 my %data = %$self;
191 122         222 my ($count);
192 122         385 foreach (keys %data) {
193 1092 100       3958 $count++ unless ($reserved{$_});
194             }
195 122 100       1093 $count ? $count : 0;
196             }
197              
198             sub get_keys {
199 206     206 1 106816 my $self = shift;
200 206         1304 my %data = %$self;
201 206         421 my @keys = ();
202 206         627 foreach (keys %data) {
203 1566 100       4152 push(@keys, $_) unless ($reserved{$_});
204             }
205 206         2262 return [ sort @keys ];
206             }
207              
208             sub get_keys_seen {
209 12     12 1 70441 my $self = shift;
210 12         207 my %data = %$self;
211 12         40 my (%seen);
212 12         69 foreach (keys %data) {
213 204 100       475 $seen{$_}++ unless ($reserved{$_});
214             }
215 12         206 return \%seen;
216             }
217              
218             ################################################################################
219             ##### &sort_by_column: called from package main to select particular fields
220             ##### to be displayed in output
221             ##### Subroutines called from within &sort_by_column
222             ################################################################################
223              
224             sub sort_by_column {
225 39     39 1 71472 my $self = shift;
226 39         75 my $columns_selected_ref = shift;
227 39         73 my %data = %{$self};
  39         618  
228 39         352 _validate_args($columns_selected_ref, \%fp);
229 38         153 $columns_selected_ref =
230             _verify_presence_of_index(\%data, $columns_selected_ref);
231 38         71 my @records;
232 38         216 foreach my $k (keys %data) {
233 706 100       2356 push (@records, $data{$k}) unless ($reserved{$k});
234             }
235 187         514 my $sortref = _sort_maker(
236 38         225 map { _make_single_comparator( $_ ) }
237 38         113 @{$columns_selected_ref}
238             );
239              
240 38         538 return _extract_columns_selected(
241             [ sort $sortref @records ],
242             $columns_selected_ref,
243             );
244             }
245              
246             sub _verify_presence_of_index {
247 38     38   207 my $dataref = shift;
248 38         64 my $columns_selected_ref = shift;
249 38         101 my @fields = @{$dataref->{fields}};
  38         8244  
250 38         73 my $index = ${$dataref}{index};
  38         97  
251 38         59 my @columns_selected = @{$columns_selected_ref};
  38         116  
252 38         101 my %cols = map {$_, 1} @columns_selected;
  186         449  
253 38 100       175 unless ($cols{$fields[$index]}) { # line 205
254 1         4100 carp "Field '$fields[$index]' which serves as unique index for records must be one of the columns selected for output; adding it to end of list of columns selected: $!";
255 1         111 push @columns_selected, $fields[$index];
256             }
257 38         201 return [ @columns_selected ];
258             }
259              
260             sub _sort_maker {
261 38     38   108 my @littlesubs = @_;
262             sub {
263 2307     2307   3301 foreach my $sub (@littlesubs) {
264 2813         4027 my $result = $sub->();
265 2813 100       11829 return $result if $result;
266             }
267 38         228 };
268             }
269              
270             sub _make_single_comparator {
271 187     187   426 my $field = shift;
272 187         651 my $sort_order = $fp{$field}->[1];
273 187         350 my $sort_type = $fp{$field}->[2];
274 187         288 my $idx = $fieldlabels{$field};
275              
276 11     11   139 no warnings qw(uninitialized numeric);
  11         130  
  11         87618  
277             my %subs = (
278             U => {
279 642     642   1539 a => sub { lc($a->[$idx]) cmp lc($b->[$idx]) },
280 1984     1984   3127 n => sub { $a->[$idx] <=> $b->[$idx] },
281 59     59   360 s => sub { $a->[$idx] cmp $b->[$idx] },
282             },
283             D => {
284 69     69   146 a => sub { lc($b->[$idx]) cmp lc($a->[$idx]) },
285 30     30   52 n => sub { $b->[$idx] <=> $a->[$idx] },
286 29     29   45 s => sub { $b->[$idx] cmp $a->[$idx] },
287             },
288 187         4110 );
289 187         3025 $subs{$sort_order}{$sort_type};
290             }
291              
292             sub _extract_columns_selected {
293 38     38   78 my ($intermed_ref, $columns_selected_ref) = @_;
294 38         69 my @results;
295 38         62 foreach my $record (@{$intermed_ref}) {
  38         492  
296 588         606 my @temp;
297 588         644 foreach my $col (@{$columns_selected_ref}) {
  588         1307  
298 3200         7449 push @temp, $record->[$fieldlabels{$col}];
299             }
300 588         5256 push @results, [ @temp ];
301             }
302 38         1165 return [ @results ];
303             }
304              
305             sub seen_one_column {
306 27     27 1 61121 my $self = shift;
307 27         352 my %data = %$self;
308 27 100       2769 croak "Invalid number of arguments to seen_one_column(): $!"
309             unless @_ == 1;
310 15         48 my $columnref = [ shift ];
311 15         65 _validate_args($columnref, \%fp);
312 11         21 my (%seen);
313 11         53 foreach (keys %data) {
314 195 100       365 unless ($reserved{$_}) {
315 161         186 $seen{ $data{$_}[ $fieldlabels{ ${$columnref}[0] } ] }++;
  161         404  
316             }
317             }
318 11         151 return \%seen;
319             }
320              
321             sub _validate_args {
322 63     63   267 my ($columns_selected_ref, $fpref) = @_;
323 63         198 my @columns_selected = @{$columns_selected_ref};
  63         184  
324 63         118 my (%seen, %unseen, @unseen);
325 63         173 foreach my $col (@columns_selected) {
326 235         987 foreach my $field (keys %$fpref) {
327 1113 100       3231 if ($col eq $field) {
328 229         522 $seen{$col} = 1;
329 229         426 last;
330             }
331             }
332 235 100       4467 $unseen{$col}++ unless $seen{$col};
333             }
334 63         326 @unseen = sort { lc($a) cmp lc($b) } (keys %unseen);
  1         8  
335 63 100       1200 croak "Invalid column selection(s): @{unseen}: $!"
336             if (@unseen);
337             }
338              
339             ################################################################################
340             ##### &select_rows: called from package main to select a particular range of
341             ##### entries from data source
342             ##### Subroutines called within &select_rows
343             ################################################################################
344              
345             sub select_rows {
346 107     107 1 25968 my ($self, $column, $relation, $choicesref) = @_;
347 107         210 my $dataref = q{};
348 107         698 $dataref = $self->_extract_rows(
349             $column, $relation, $choicesref, \%fp, \%fieldlabels,
350             \&_analyze_relation, \&_strip_non_matches);
351 104         1474 %$self = %$dataref;
352 104         530 return $self;
353             }
354              
355             sub _analyze_relation { # Analysis of $relation: passed by ref to subclass
356 106     106   3544 my ($relation_raw, $sorttype) = @_;
357 106         148 my ($type, $relation_confirmed);
358 106 100       710 croak "Relation \'$relation_raw\' has not yet been added to\nData::Presenter's internal specifications. $!"
359             unless $all_relations{$relation_raw};
360 105         433 $type = $sortclass{$all_relations{$relation_raw}}{$sorttype};
361 105 50       257 croak "Problem with sort type $type: $!"
362             unless $type;
363 105         159 $relation_confirmed = $type;
364 105         1106 return ($relation_confirmed, \%gt_lt_ops);
365             }
366              
367             sub _action {
368 1136     1136   3125 my ($relation, $seenref, $item, $dataref, $record, $correctedref) = @_;
369             my %delete_instructions = ( # dispatch table
370 81         1202 'eq' => sub { delete ${$dataref}{$record}
  91         428  
371 91 100   91   156 unless exists ${$seenref}{$item} },
372 72         1228 '==' => sub { delete ${$dataref}{$record}
  88         404  
373 88 100   88   76 unless exists ${$seenref}{$item} },
374 98         1604 'ne' => sub { delete ${$dataref}{$record}
  209         2714  
375 209 100   209   230 unless ! exists ${$seenref}{$item} },
376 126         1592 '!=' => sub { delete ${$dataref}{$record}
  198         1330  
377 198 100   198   193 unless ! exists ${$seenref}{$item} },
378 35         1155 'lt' => sub { delete ${$dataref}{$record}
  55         8778  
379 55 100   55   70 unless $item lt ${$correctedref}[0] },
380 40         455 '<' => sub { delete ${$dataref}{$record}
  55         275  
381 55 100   55   51 unless $item < ${$correctedref}[0] },
382 35         472 'gt' => sub { delete ${$dataref}{$record}
  55         366  
383 55 100   55   57 unless $item gt ${$correctedref}[0] },
384 40         529 '>' => sub { delete ${$dataref}{$record}
  55         276  
385 55 100   55   58 unless $item > ${$correctedref}[0] },
386 36         567 'le' => sub { delete ${$dataref}{$record}
  66         757  
387 66 100   66   89 unless $item le ${$correctedref}[0] },
388 42         477 '<=' => sub { delete ${$dataref}{$record}
  66         379  
389 66 100   66   62 unless $item <= ${$correctedref}[0] },
390 31         562 'ge' => sub { delete ${$dataref}{$record}
  85         1026  
391 85 100   85   105 unless $item ge ${$correctedref}[0] },
392 82         1169 '>=' => sub { delete ${$dataref}{$record}
  113         727  
393 113 100   113   111 unless $item >= ${$correctedref}[0] },
394 1136         33591 );
395 1136         2173 &{$delete_instructions{$relation}};
  1136         2129  
396             }
397             sub _strip_non_matches {
398 104     104   2317 my ($dataref, $flref, $column, $relation, $correctedref, $seenref) = @_;
399 104         134 foreach my $record (keys %{$dataref}) {
  104         1078  
400 1448 100       4070 unless ($reserved{$record}) {
401 1136         1677 my $item = ${$dataref}{$record}[${$flref}{$column}];
  1136         2081  
  1136         1833  
402 1136         2366 _action( $relation, $seenref, $item, $dataref,
403             $record, $correctedref);
404             }
405             }
406 104         465 return $dataref;
407             }
408              
409             ################################################################################
410             ##### Methods for simple output
411             ##### and subroutines called within those methods
412             ################################################################################
413              
414             sub print_to_screen {
415 7     7 1 53397 my $class = shift;
416 7         110 my %data = %$class;
417 7         49 _print_engine(\%data, \%reserved);
418 7         39 return 1;
419             }
420              
421             sub print_to_file {
422 9     9 1 15401 my ($class, $outputfile) = @_;
423 9         952 my %data = %$class;
424 9         26 my $OUT;
425 9         133 my $oldfh = select $OUT;
426 9 50       1484 open($OUT, ">$outputfile")
427             || croak "Cannot open $outputfile for writing: $!";
428 9         49 _print_engine(\%data, \%reserved);
429 9 50       635 close($OUT) || croak "Cannot close $outputfile: $!";
430 9         36 select($oldfh);
431 9         77 return 1;
432             }
433              
434             sub _print_engine {
435 16     16   39 my ($dataref, $reservedref) = @_;
436 16         140 my %data = %$dataref;
437 16         88 my %reserved = %$reservedref;
438 16         38 local $_;
439 16         164 foreach my $i (sort keys %data) {
440 174 100       662 unless ($reserved{$i}) {
441 126         132 print "$_;" foreach (@{$data{$i}});
  126         968  
442 126         3403 print "\n";
443             }
444             }
445             }
446              
447             sub print_with_delimiter {
448 5     5 1 3451 my ($class, $outputfile, $delimiter) = @_;
449 5         51 my %data = %$class;
450 5 50       407 open (my $OUT, ">$outputfile")
451             || croak "Cannot open $outputfile for writing: $!";
452 5         66 foreach my $i (sort keys %data) {
453 62 100       155 unless ($reserved{$i}) {
454 47         48 my @fields = @{$data{$i}};
  47         174  
455 47         120 for (my $j=0; $j < scalar(@fields); $j++) {
456 448 100       681 if ($j < scalar(@fields) - 1) {
457 401 100       541 if ($fields[$j]) {
458 325         1104 print $OUT $fields[$j], $delimiter;
459             } else {
460 76         165 print $OUT $delimiter;
461             }
462             } else {
463 47 100       164 print $OUT $fields[$j] if ($fields[$j]);
464             }
465             }
466 47         138 print $OUT "\n";
467             }
468             }
469 5 50       245 close ($OUT) || croak "Cannot close $outputfile: $!";
470 5         45 return 1;
471             }
472              
473             sub full_report {
474 5     5 1 2978 my ($class, $outputfile);
475 5         17 my %data = ();
476 5         15 my @fields = ();
477 5         13 ($class, $outputfile) = @_;
478 5         48 %data = %$class;
479 5         14 @fields = @{$data{'fields'}};
  5         32  
480 5 50       419 open (my $OUT, ">$outputfile")
481             || croak "Cannot open $outputfile for writing: $!";
482 5         66 foreach my $i (sort keys %data) {
483 62 100       147 unless ($reserved{$i}) {
484 47         106 print $OUT "$i\n";
485 47         124 for (my $j=0; $j <= $#fields; $j++) {
486 448         811 print $OUT " $fields[$j]", ' ' x (24 - length($fields[$j]));
487 448         1137 print $OUT "$data{$i}[$j]\n";
488             }
489 47         81 print $OUT "\n";
490             }
491             }
492 5 50       242 close ($OUT) || croak "Cannot close $outputfile: $!";
493 5         46 return 1;
494             }
495              
496             ################################################################################
497             ##### Methods which output data like Perl formats
498             ##### and subroutines called from within those methods
499             ################################################################################
500              
501             our %keys_needed_to_write = (
502             'Data::Presenter::writeformat'
503             => [ qw| sorted columns file | ],
504             'Data::Presenter::writeformat_plus_header'
505             => [ qw| sorted columns file title | ],
506             'Data::Presenter::writeformat_with_reprocessing'
507             => [ qw| sorted columns file reprocess | ],
508             'Data::Presenter::writeformat_deluxe'
509             => [ qw| sorted columns file title reprocess | ],
510             'Data::Presenter::writedelimited'
511             => [ qw| sorted file delimiter | ],
512             'Data::Presenter::writedelimited_plus_header'
513             => [ qw| sorted columns file delimiter | ],
514             'Data::Presenter::writedelimited_with_reprocessing'
515             => [ qw| sorted columns file reprocess delimiter | ],
516             'Data::Presenter::writedelimited_deluxe'
517             => [ qw| sorted columns file reprocess delimiter | ],
518             'Data::Presenter::writeHTML'
519             => [ qw| sorted columns file title | ],
520             );
521              
522             sub _validate_write_args {
523 68     68   726 my $callingsub = (caller(1))[3];
524 68         310 my @incoming = @_;
525 68 100       429 croak "Method $callingsub needs even number of arguments: $!"
526             if (@incoming % 2);
527 67         436 my %args = @incoming;
528 67 100       701 croak "Method $callingsub needs key-value pairs:\n @{ $keys_needed_to_write{$callingsub} } \n $!"
  8         2935  
529             unless (is_LsubsetR( [
530             $keys_needed_to_write{$callingsub},
531             [ keys %args ],
532             ] ) );
533 59         15837 return %args;
534             }
535              
536             sub writeformat {
537 12     12 1 23340 my $self = shift;
538 12         67 my %args = _validate_write_args(@_);
539 10         76 my $picline = _format_picture_line($args{columns});
540 10 50       1809 open (my $REPORT, ">$args{file}") || croak "cannot create $args{file}: $!";
541 10         31 foreach my $record (@{$args{sorted}}) {
  10         57  
542 149         384 local $^A = q{};
543 149         144 formline($picline, @{$record});
  149         482  
544 149         541 print $REPORT $^A, "\n";
545             }
546 10 50       1187 close ($REPORT) || croak "can't close $args{file}:$!";
547 10         107 return 1;
548             }
549              
550             sub writeformat_plus_header {
551 11     11 1 42728 my $self = shift;
552 11         59 my %args = _validate_write_args(@_);
553 10         81 my $title_out = _format_title($args{title});
554 10         49 my $argument_line_top_ref =
555             _format_argument_line_top($args{columns});
556 10         48 my $hyphen_line = _format_hyphen_line($args{columns});
557 10         43 my $picline = _format_picture_line($args{columns});
558 10 50       1308 open (my $REPORT, ">$args{file}") || croak "cannot create $args{file}: $!";
559 10         145 print $REPORT $title_out, "\n\n";
560 10         24 print $REPORT "$_\n" foreach (@{$argument_line_top_ref});
  10         64  
561 10         28 print $REPORT $hyphen_line, "\n";
562 10         20 foreach my $record (@{$args{sorted}}) {
  10         33  
563 149         281 local $^A = q{};
564 149         135 formline($picline, @{$record});
  149         753  
565 149         460 print $REPORT $^A, "\n";
566             }
567 10 50       570 close ($REPORT) || croak "can't close $args{file}:$!";
568 10         217 return 1;
569             }
570              
571             sub writeformat_with_reprocessing {
572 5     5 1 6527 my $self = shift;
573 5         27 my %args = _validate_write_args(@_);
574 4         222 my %data = %$self;
575              
576 4         53 my ($substr_data_ref, $picline) = _prepare_to_reprocess(
577             $args{reprocess}, \%fp, \%data, $args{columns});
578              
579 1 50       165 open (my $REPORT, ">$args{file}") || croak "cannot create $args{file}: $!";
580 1         3 foreach my $record (@{$args{sorted}}) {
  1         10  
581 83         237 local $^A = q{};
582 83         88 formline($picline, @{$record});
  83         337  
583 83         262 my $line = $self->_reprocessor(
584             $^A, # the formed line
585             $substr_data_ref, # the points at which I have to splice out
586             # text from the formed line and amount thereof
587             );
588 83         18082 print $REPORT $line, "\n";
589             }
590 1 50       80 close ($REPORT) || croak "can't close $args{file}:$!";
591 1         45 return 1;
592             }
593              
594             sub writeformat_deluxe {
595 2     2 1 5168 my $self = shift;
596 2         13 my %args = _validate_write_args(@_);
597 1         61 my %data = %$self;
598              
599 1         11 my ($substr_data_ref, $picline) = _prepare_to_reprocess(
600             $args{reprocess}, \%fp, \%data, $args{columns});
601              
602 1         2 my (@header, @accumulator);
603 1         6 my $title_out = _format_title($args{title});
604 1         6 my $argument_line_top_ref =
605             _format_argument_line_top($args{columns}, $args{reprocess});
606 1         7 my $hyphen_line = _format_hyphen_line($args{columns}, $args{reprocess});
607 1         3 @header = ($title_out, q{}, @{$argument_line_top_ref}, $hyphen_line);
  1         13  
608              
609 1         2 foreach my $record (@{$args{sorted}}) {
  1         4  
610 83         204 local $^A = q{};
611 83         92 formline($picline, @{$record});
  83         338  
612 83         246 my $line = $self->_reprocessor(
613             $^A, # the formed line
614             $substr_data_ref, # the points at which I have to splice out
615             # text from the formed line and amount thereof
616             );
617 83         18111 push @accumulator, $line;
618             }
619 1 50       172 open (my $REPORT, ">$args{file}") || croak "cannot create $args{file}: $!";
620 1         134 print $REPORT $_, "\n" foreach (@header, @accumulator);
621 1 50       64 close ($REPORT) || croak "can't close $args{file}:$!";
622 1         52 return 1;
623             }
624              
625             sub _prepare_to_reprocess {
626 5     5   13 my ($reprocessref, $fpref, $dataref, $columns_selected_ref) = @_;
627 5         11 my %reprocessing_info = %{$reprocessref};
  5         33  
628 5         10 my %fp = %{$fpref};
  5         43  
629 5         15 my %data = %{$dataref};
  5         146  
630 5         33 my @columns_selected = @{$columns_selected_ref};
  5         23  
631              
632             # We must validate the info passed in thru $reprocessref.
633             # This is a multi-stage process.
634             # 1: Verify that the fields requested for reprocessing exist as
635             # fields in the configuration file.
636 5         43 my @fields_for_reprocessing = sort keys %reprocessing_info;
637 5         28 _validate_args(\@fields_for_reprocessing, \%fp);
638              
639              
640             # 2: Verify that there exists a subroutine named &reprocess_[field]
641             # whose name has been stored as a key in defined in
642             # %{$data{'options'}{'subs'}}.
643 45         209 my @confirmed_subs =
644 5         10 grep {s/^reprocess_(.*)/$1/} keys %{$data{'options'}{'subs'}};
  5         42  
645 5 100       32 croak "You are trying to reprocess fields for which no reprocessing subroutines yet exist: $!"
646             unless (is_LsubsetR( [
647             \@fields_for_reprocessing,
648             \@confirmed_subs
649             ] ) );
650              
651             # 3: Verify that we can tap into the data sources referenced in
652             # %{$data{'options'}{'sources'}} for each field needing reprocessing
653 4         900 my @available_sources = sort keys %{$data{'options'}{'sources'}};
  4         33  
654 4 100       22 croak "You are trying to reprocess fields for which no original data sources are available: $!"
655             unless (is_LsubsetR( [
656             \@fields_for_reprocessing,
657             \@available_sources
658             ] ) );
659              
660             # 4: Verify that the file mentioned in the values-arrays of
661             # %reprocessing_info exists, and that appropriate digits are entered
662             # for the fixed-length of the replacement string.
663 3         620 foreach (sort keys %reprocessing_info) {
664 6 100       187 croak "Fixed length of replacement string is misspecified;\n Must be all digits: $!"
665             unless $reprocessing_info{$_} =~ /^\d+$/;
666             }
667              
668 2         8 my %args_indices = ();
669 2         10 for (my $h=0; $h<=$#columns_selected; $h++) {
670 12         41 $args_indices{$columns_selected[$h]} = $h;
671             }
672              
673 2         5 my %substr_data = ();
674 2         9 foreach (sort keys %reprocessing_info) {
675             # 1st: Determine the position in the formed string where the
676             # old field began, as well as its length
677             # Given that I used a single whitespace to separate fields in
678             # the formed line, the starting position is the sum of the number of
679             # fields preceding the target field in the formed line
680             # PLUS the combined length of those fields
681 4         7 my ($comb_length, $start);
682              
683 4 100       15 if ($args_indices{$_} == 0) {
684 2         4 $start = $args_indices{$_};
685             } else {
686 2         9 for (my $j=0; $j<$args_indices{$_}; $j++) {
687 2         10 $comb_length += $fp{$columns_selected[$j]}[0];
688             }
689 2         5 $start = $args_indices{$_} + $comb_length;
690             }
691 4         25 $substr_data{$start} = [
692             $_,
693             $fp{$_}[0],
694             $reprocessing_info{$_},
695 4         10 \%{ $data{'options'}{'sources'}{$_} },
696             $dataref, # new in v0.51
697             ];
698             }
699 2         12 my $picline = _format_picture_line(\@columns_selected);
700 2         40 return (\%substr_data, $picline);
701             }
702              
703             ################################################################################
704             ##### Methods which output data with delimiters between fields
705             ##### and subroutines called within those methods
706             ################################################################################
707              
708             sub writedelimited {
709 11     11 1 94691 my $self = shift;
710 11         63 my %args = _validate_write_args(@_);
711 10 50       1871 open (my $REPORT, ">$args{file}") || croak "cannot create $args{file}: $!";
712 10         31 foreach my $record (@{$args{sorted}}) {
  10         44  
713 149         175 print $REPORT join($args{delimiter}, @{$record}), "\n";
  149         468  
714             }
715 10 50       578 close ($REPORT) || croak "can't close $args{file}:$!";
716 10         100 return 1;
717             }
718              
719             sub writedelimited_plus_header {
720 11     11 1 43342 my $self = shift;
721 11         54 my %args = _validate_write_args(@_);
722 10         68 my $header =
723             _format_argument_line_top_delimited($args{columns}, $args{delimiter});
724 10 50       1205 open (my $REPORT, ">$args{file}") || croak "cannot create $args{file}: $!";
725 10         108 print $REPORT "$header\n";
726 10         25 foreach my $record (@{$args{sorted}}) {
  10         58  
727 149         191 print $REPORT join($args{delimiter}, @{$record}), "\n";
  149         6201  
728             }
729 10 50       562 close ($REPORT) || croak "can't close $args{file}:$!";
730 10         111 return 1;
731             }
732              
733             sub writedelimited_with_reprocessing {
734 4     4 1 10307 my $self = shift;
735 4         15 my %args = _validate_write_args(@_);
736 3         9 my $dataref = \%{$self};
  3         9  
737              
738 3         18 _prepare_to_reprocess_delimit(
739             $args{reprocess}, \%fp, $dataref, $args{columns});
740              
741 1         209 my %cols_select_labels = ();
742 1         3 for (my $i = 0; $i <= $#{$args{columns}}; $i++) {
  7         19  
743 6         7 $cols_select_labels{${$args{columns}}[$i]} = $i;
  6         12  
744             }
745 1         1 my @revised;
746 1         2 foreach my $record (@{$args{sorted}}) {
  1         7  
747 83         5266 push @revised, $self->_reprocessor_delimit(
748             $record, $args{reprocess}, \%cols_select_labels, $dataref);
749             }
750 1 50       212 open my $OUT, ">$args{file}"
751             or croak "Couldn't open $args{file} for writing: $!";
752 1         4 foreach my $rev (@revised) {
753 83         89 print $OUT (join $args{delimiter}, @{$rev}), "\n";
  83         212  
754             }
755 1 50       88 close $OUT or croak "Couldn't close $args{file} after writing: $!";
756 1         52 return 1;
757             }
758              
759             sub writedelimited_deluxe {
760 2     2 1 4726 my $self = shift;
761 2         16 my %args = _validate_write_args(@_);
762 1         3 my $dataref = \%{$self};
  1         3  
763              
764 1         7 _prepare_to_reprocess_delimit(
765             $args{reprocess}, \%fp, $dataref, $args{columns});
766              
767 1         194 my %cols_select_labels = ();
768 1         2 for (my $i = 0; $i <= $#{$args{columns}}; $i++) {
  7         17  
769 6         93 $cols_select_labels{${$args{columns}}[$i]} = $i;
  6         13  
770             }
771 1         2 my @revised;
772 1         2 foreach my $record (@{$args{sorted}}) {
  1         5  
773 83         5393 push @revised, $self->_reprocessor_delimit(
774             $record, $args{reprocess}, \%cols_select_labels, $dataref);
775             }
776 1         57 my $header = _format_argument_line_top_delimited(
777             $args{columns}, $args{delimiter}
778             );
779 1 50       171 open my $OUT, ">$args{file}"
780             or croak "Couldn't open $args{file} for writing: $!";
781 1         23 print $OUT "$header\n";
782 1         3 foreach my $rev (@revised) {
783 83         114 print $OUT (join $args{delimiter}, @{$rev}), "\n";
  83         184  
784             }
785 1 50       79 close $OUT or croak "Couldn't close $args{file} after writing: $!";
786 1         47 return 1;
787             }
788              
789             sub _prepare_to_reprocess_delimit {
790 4     4   12 my ($reprocessref, $fpref, $dataref, $columns_selected_ref) = @_;
791 4         7 my %fp = %{$fpref};
  4         35  
792 4         10 my %data = %{$dataref};
  4         173  
793 4         26 my @columns_selected = @{$columns_selected_ref};
  4         18  
794              
795             # We must validate the info passed in thru $reprocessref.
796             # This is a multi-stage process.
797             # 1: Verify that the fields requested for reprocessing exist as
798             # fields in the configuration file.
799 4         17 _validate_args($reprocessref, \%fp);
800              
801             # 2: Verify that there exists a subroutine named
802             # &reprocess_delimit_[field]
803             # whose name has been stored as a key in defined in
804             # %{$data{'options'}{'subs'}}.
805 36         122 my @confirmed_subs =
806 4         6 grep {s/^reprocess_delimit_(.*)/$1/} keys %{$data{'options'}{'subs'}};
  4         30  
807 4 100       23 croak "You are trying to reprocess fields for which no reprocessing subroutines yet exist: $!"
808             unless (is_LsubsetR( [
809             $reprocessref,
810             \@confirmed_subs,
811             ] ) );
812              
813             # 3: Verify that we can tap into the data sources referenced in
814 3         531 my @available_sources = sort keys %{$data{'options'}{'sources'}};
  3         28  
815 3 100       15 croak "You are trying to reprocess fields for which no original data sources are available: $!"
816             unless (is_LsubsetR( [
817             $reprocessref,
818             \@available_sources,
819             ] ) );
820             }
821              
822             sub _format_title {
823 11     11   28 my $title_raw = shift;
824 11         23 my $title = $title_raw;
825 11         28 return $title;
826             }
827              
828             sub _format_argument_line_top {
829 20     20   49 my $columns_selected_ref = shift;
830 20 100       78 my $reprocessref = shift if $_[0];
831 20         182 my @args = @$columns_selected_ref;
832 20         42 my @lines = ();
833 20         68 my $j = q{}; # index of the arg requested for printout currently
834             # being processed
835 20         211 for ($j = 0; $j < scalar(@args); $j++) {
836 122         160 my $n = 0; # current line being assigned to, starting with 0
837 122         286 my $label = $fp{$args[$j]}[3]; # easier to read
838 122         391 my $max = defined ${$reprocessref}{$args[$j]}
  2         4  
839 122 100       245 ? ${$reprocessref}{$args[$j]}
840             : $fp{$args[$j]}[0];
841 122         180 my $remain = $label; # at the outset, the entire label
842             # remains to be allocated to the proper line
843 122         175 my @overage = ();
844             # first see if any words in $remain need to be truncated
845 122         590 my @remainwords = split(/\s/, $remain);
846 122         226 foreach my $word (@remainwords) {
847 230 100       586 $word = substr($word, 0, $max) if (length($word) > $max);
848             }
849 122         248 $remain = join ' ', @remainwords;
850 122         325 while ($remain) {
851 145 100       351 if (length($remain) <= $max) {
852             # entire remainder of label will be placed on current line
853 122         568 $lines[$n][$j] = $remain . ' ' x ($max - length($remain));
854 122         565 $remain = q{};
855             } else {
856             # entire remainder of label cannot fit on current line
857 23         54 my $word = q{};
858 23         123 my @labelwords = split(/\s/, $remain);
859 23         259 until (length($remain) <= $max) {
860 23         43 $word = shift(@labelwords);
861 23         56 push (@overage, $word);
862 23         74 $remain = join ' ', @labelwords;
863             }
864 23         98 $lines[$n][$j] = $remain . ' ' x ($max - length($remain));
865 23         55 $remain = join ' ', @overage ;
866 23         42 @overage = ();
867 23         71 $n++;
868             }
869             }
870             }
871 20         37 my (@column_heads);
872 20         47 foreach my $p (reverse @lines) {
873 39         124 for ($j = 0; $j < scalar(@args); $j++) {
874 238         733 my $max = defined ${$reprocessref}{$args[$j]}
  2         5  
875 238 100       253 ? ${$reprocessref}{$args[$j]}
876             : $fp{$args[$j]}[0];
877 238 100       257 if (! ${$p}[$j]) {
  238         845  
878 93         185 ${$p}[$j] = ' ' x $max;
  93         1954  
879             }
880             }
881 39         352 my $part = join ' ', @$p;
882 39         108 push @column_heads, $part;
883             }
884 20         113 return \@column_heads;
885             }
886              
887             sub _format_argument_line_top_delimited {
888 11     11   33 my ($columns_selected_ref, $delimiter) = @_;
889 11         24 my @temp;
890 11         24 foreach my $col (@{$columns_selected_ref}) {
  11         55  
891 67         220 push @temp, $fp{$col}[3];
892             }
893 11         50 my $header = join($delimiter, @temp);
894 11         38 return $header;
895             }
896              
897             sub _format_hyphen_line {
898 20     20   40 my $columns_selected_ref = shift;
899 20 100       66 my $reprocessref = shift if $_[0];
900 20         37 my $hyphen_line_length = 0;
901 20         45 my $hyphen_line = q{};
902 20         53 foreach my $h (@$columns_selected_ref) {
903 122         437 $hyphen_line_length += defined ${$reprocessref}{$h}
  2         5  
904 122 100       135 ? (${$reprocessref}{$h} + 1)
905             : ($fp{$h}->[0] + 1);
906             }
907 20         67 $hyphen_line = '-' x ($hyphen_line_length - 1);
908 20         50 return $hyphen_line;
909             }
910              
911             sub _format_picture_line {
912 22     22   49 my $columns_selected_ref = shift;
913 22         54 my $line = q{};
914 22         41 my $g = 0; # counter
915 22         213 foreach my $h (@$columns_selected_ref) {
916 134         178 my $picture = q{};
917 134 100       636 if ($fp{$h}[2] =~ /^n$/i) {
918 48         132 $picture = '@' . '>' x ($fp{$h}[0] - 1);
919             } else {
920 86         196 $picture = '@' . '<' x ($fp{$h}[0] - 1);
921             }
922 134 100       237 if ($g < $#{$columns_selected_ref}) {
  134         457  
923 112         520 $line .= $picture . q{ };
924 112         194 $g++;
925             } else {
926 22         70 $line .= $picture;
927             }
928             }
929 22         59 return $line;
930             }
931              
932             ################################################################################
933             ##### Subroutines involved in writing HTML
934             ################################################################################
935              
936             sub writeHTML {
937 10     10 1 34578 my $self = shift;
938 10         56 my %args = _validate_write_args(@_);
939 10         56 my %max = (); # keys will be indices of @{$args{columns}};
940             # values will be max space allocated from %parameters
941 10         36 for (my $j = 0; $j < scalar(@{$args{columns}}); $j++) {
  70         199  
942 60         83 $max{$j} = $fp{${$args{columns}}[$j]}[0];
  60         246  
943             }
944 10 100       251 croak "Name of output file must end in .html or .htm $!"
945             unless ($args{file} =~ /\.html?$/);
946 9 50       1081 open(my $HTML, ">$args{file}")
947             || croak "cannot open $args{file} for writing: $!";
948 9         166 print $HTML <
949            
950            
951             $args{title}
952            
953            
954            
955            
956            
957             bgcolor="#cc0066">
958             color="#ff99cc">   $args{title}
959            
960            
961            
962             END_OF_HTML1
963 9         51 my $argument_line_top_ref =
964             _format_argument_line_top($args{columns});
965 9         48 my $hyphen_line = _format_hyphen_line($args{columns});
966 9         34 print $HTML '
', "\n"; 
967 9         18 print $HTML $_, '', "\n" foreach (@{$argument_line_top_ref});
  9         50  
968 9         33 print $HTML "$hyphen_line", '', "\n";
969 9         36 foreach my $row (@{$args{sorted}}) {
  9         39  
970 66         75 my @values = @{$row};
  66         242  
971 66         98 my @paddedvalues = ();
972 66         96 for (my $j = 0; $j < scalar(@{$args{columns}}); $j++) {
  472         1110  
973 406         454 my $newvalue = q{};
974 406 100       424 if ($fp{${$args{columns}}[$j]}[2] =~ /^n$/i) {
  406         1352  
975 132         362 $newvalue =
976             (' ' x ($max{$j} - length($values[$j]))) .
977             $values[$j] . ' ';
978             } else { #
979 274         677 $newvalue =
980             $values[$j] .
981             (' ' x ($max{$j} - length($values[$j]) + 1));
982             }
983 406         793 push(@paddedvalues, $newvalue);
984             }
985 66         78 chop $paddedvalues[(scalar(@{$args{columns}})) - 1];
  66         128  
986 66         316 print $HTML @paddedvalues, '', "\n";
987             }
988 9         25 print $HTML ' ', "\n";
989 9         24 print $HTML <
990            
991            
992             END_OF_HTML2
993 9 50       544 close($HTML) || croak "cannot close $args{file}: $!";
994 9         105 return 1;
995             }
996              
997             1;
998              
999             ######################################################################
1000             ##### DOCUMENTATION #####
1001             ######################################################################
1002              
1003             =head1 NAME
1004              
1005             Data::Presenter - Reformat database reports
1006              
1007             =head1 VERSION
1008              
1009             This document refers to version 1.03 of Data::Presenter, which consists of
1010             Data::Presenter.pm and various packages subclassed thereunder, most notably
1011             Data::Presenter::Combo.pm and its subclasses
1012             Data::Presenter::Combo::Intersect.pm and Data::Presenter::Combo::Union.pm.
1013             This version was released February 10, 2008.
1014              
1015             =head1 SYNOPSIS
1016              
1017             use Data::Presenter;
1018             use Data::Presenter::[Package1]; # example: use Data::Presenter::Census
1019              
1020             our (@fields, %parameters, $index);
1021             $configfile = 'fields.XXX.data';
1022             do $configfile;
1023              
1024             $dp1 = Data::Presenter::[Package1]->new(
1025             $sourcefile, \@fields,\%parameters, $index
1026             );
1027              
1028             $data_count = $dp1->get_data_count();
1029              
1030             $dp1->print_data_count();
1031              
1032             $keysref = $dp1->get_keys();
1033              
1034             $seenref = $dp1->get_keys_seen();
1035              
1036             $dp1->print_to_screen();
1037              
1038             $dp1->print_to_file($outputfile);
1039              
1040             $dp1->print_with_delimiter($outputfile, $delimiter);
1041              
1042             $dp1->full_report($outputfile);
1043              
1044             $dp1->select_rows($column, $relation, \@choices);
1045              
1046             $sorted_data = $dp1->sort_by_column(\@columns_selected);
1047              
1048             $seen_hash_ref = $dp1->seen_one_column($column);
1049              
1050             $dp1->writeformat(
1051             sorted => $sorted_data,
1052             columns => \@columns_selected,
1053             file => $outputfile,
1054             );
1055              
1056             $dp1->writeformat_plus_header(
1057             sorted => $sorted_data,
1058             columns => \@columns_selected,
1059             file => $outputfile,
1060             title => $title,
1061             );
1062              
1063             %reprocessing_info = (
1064             lastname => 17,
1065             firstname => 15,
1066             );
1067              
1068             $dp1->writeformat_with_reprocessing(
1069             sorted => $sorted_data,
1070             columns => \@columns_selected,
1071             file => $outputfile,
1072             reprocess => \%reprocessing_info,
1073             );
1074              
1075             $dp1->writeformat_deluxe(
1076             sorted => $sorted_data,
1077             columns => \@columns_selected,
1078             file => $outputfile,
1079             title => $title,
1080             reprocess => \%reprocessing_info,
1081             );
1082              
1083             $dp1->writedelimited(
1084             sorted => $sorted_data,
1085             file => $outputfile,
1086             delimiter => $delimiter,
1087             );
1088              
1089             $dp1->writedelimited_plus_header(
1090             sorted => $sorted_data,
1091             columns => \@columns_selected,
1092             file => $outputfile,
1093             delimiter => $delimiter,
1094             );
1095              
1096             @reprocessing_info = qw( instructor timeslot room );
1097              
1098             $dp1->writedelimited_with_reprocessing(
1099             sorted => $sorted_data,
1100             columns => \@columns_selected,
1101             file => $outputfile,
1102             delimiter => $delimiter,
1103             reprocess => \@reprocessing_info,
1104             );
1105              
1106             $dp1->writedelimited_deluxe(
1107             sorted => $sorted_data,
1108             columns => \@columns_selected,
1109             file => $outputfile,
1110             delimiter => $delimiter,
1111             reprocess => \@reprocessing_info,
1112             );
1113              
1114             $dp1->writeHTML(
1115             sorted => $sorted_data,
1116             columns => \@columns_selected,
1117             file => 'somename.html',
1118             title => $title,
1119             );
1120              
1121             Data::Presenter::Combo objects:
1122              
1123             use Data::Presenter;
1124             use Data::Presenter::[Package1]; # example: use Data::Presenter::Census
1125             use Data::Presenter::[Package2]; # example: use Data::Presenter::Medinsure
1126              
1127             our (@fields, %parameters, $index);
1128             $configfile = 'fields.XXX.data';
1129             do $configfile;
1130              
1131             $dp1 = Data::Presenter::[Package1]->new(
1132             $sourcefile, \@fields,\%parameters, $index
1133             );
1134              
1135             # different source file and configuration file
1136              
1137             $configfile = 'fields.YYY.data';
1138             do $configfile;
1139              
1140             $dp2 = Data::Presenter::[Package2]->new(
1141             $sourcefile, \@fields,\%parameters, $index);
1142              
1143             @objects = ($dp1, $dp2);
1144             $dpC = Data::Presenter::Combo::Intersect->new(\@objects);
1145             $dpC = Data::Presenter::Combo::Union->new(\@objects);
1146              
1147             =head2 Notice of Changes of Interface
1148              
1149             If you have I used Data::Presenter prior to version 1.0, skip this
1150             section.
1151              
1152             =head3 C-Family of Methods Now Takes List of Key-Value Pairs
1153              
1154             Since the last publicly available version of Data::Presenter (0.68), the
1155             interface to nine of its public methods has been changed. Previously, methods
1156             in the C-family of methods took a list of arguments which had
1157             to be provided in a very specific order. For example, C
1158             took five arguments:
1159              
1160             $dp1->writeformat_deluxe(
1161             $sorted_data,
1162             \@columns_selected,
1163             $outputfile,
1164             $title,
1165             \%reprocessing_info
1166             );
1167              
1168             As the number of elements in the list of arguments increases, it becomes more
1169             difficult to remember the order in which they must be passed. At a certain
1170             point it becomes easier to pass the arguments in the form of key-value pairs.
1171             As long as each pair is correctly specified, the order of the pairs no longer
1172             matters. C, for example, now has this interface:
1173              
1174             $dp1->writeformat_deluxe(
1175             sorted => $sorted_data,
1176             columns => \@columns_selected,
1177             file => $outputfile,
1178             title => $title,
1179             reprocess => \%reprocessing_info,
1180             );
1181              
1182             Please study the L<"SYNOPSIS"> above to see how to revise your calls to
1183             methods with C, C or C in their names.
1184              
1185             =head3 Change in Assignment of C<$index> in C
1186              
1187             Data::Presenter is used by writing and using a subclass in which a new object
1188             is created. Each such subclass must hold an C<_init()> method and each such
1189             C<_init()> method must accomplish certain tasks. One of these tasks is to
1190             store the value of C<$index> (found in the configuration file) in the object
1191             being created. In versions 0.68 and earlier, the code which did this looked
1192             like this:
1193              
1194             $data{'index'} = [$index];
1195              
1196             In other words, C<$index> was not directly assigned to the hash holding the
1197             Data::Presenter::[Package1] object's data. Instead, a reference to a
1198             one-element array holding C<$index> was passed.
1199              
1200             This has now been simplified:
1201              
1202             $data{'index'} = $index;
1203              
1204             In other words, simply assign C<$index>; no reference is needed. See the
1205             sample packages included under the F directory in this distribution for a
1206             live presentation of this change.
1207              
1208             =head1 PREREQUISITES
1209              
1210             Data::Presenter requires Perl 5.6 or later. The module and its test suite
1211             require the following modules from CPAN:
1212              
1213             =over 4
1214              
1215             =item List::Compare
1216              
1217             By the same author as Data::Presenter:
1218             L.
1219              
1220             =item IO::Capture
1221              
1222             Used only in the test suite to capture output printed to screen by
1223             Data::Presenter methods. By Mark Reynolds and Jon Morgan.
1224             L.
1225              
1226             =item IO::Capture::Extended
1227              
1228             Used only in the test suite to capture output printed to screen by
1229             Data::Presenter methods. By the same author as Data::Presenter. Has
1230             IO::Capture (above) as prerequisite.
1231             L.
1232              
1233             =item Tie::File
1234              
1235             Used only in the test suite to validate text printed to files by
1236             Data::Presenter methods. By Mark-Jason Dominus. Distributed with Perl since
1237             5.7.3; otherwise, available from CPAN: L.
1238              
1239             =back
1240              
1241             Each of the prerequisites is pure Perl and should install with the
1242             F shell by typing 'y' at the prompts as needed.
1243              
1244             =head1 DESCRIPTION
1245              
1246             Data::Presenter is an object-oriented module useful for the
1247             reformatting of already formatted text files such as reports generated by
1248             database programs. If the data can be represented by a
1249             row-column matrix, where for each data entry (row):
1250              
1251             =over 4
1252              
1253             =item *
1254              
1255             there are one or more fields containing data values (columns); and
1256              
1257             =item *
1258              
1259             at least one of those fields can be used as an index to uniquely identify
1260             each entry,
1261              
1262             =back
1263              
1264             then the data structure is suitable for manipulation by Data::Presenter.
1265             In Perl terms, if the data can be represented by a I, it is
1266             suitable for reformatting with Data::Presenter.
1267              
1268             Data::Presenter can be used to output some fields (columns) from a database
1269             while excluding others (see L<"sort_by_column()"> below). It can also be used
1270             to select certain entries (rows) from the database for output while excluding
1271             other entries (see L<"select_rows()"> below).
1272              
1273             In addition, if a user has two or more database reports, each of which has
1274             the same field serving as an index for the data, then it is possible to
1275             construct either a:
1276              
1277             =over 4
1278              
1279             =item *
1280              
1281             L object
1282             which holds data for those entries found in common in all the source
1283             databases (the I of the entries in the source databases); or a
1284              
1285             =item *
1286              
1287             L object
1288             which holds data for those entries found in any of the source databases (the
1289             I of the entries in the source databases).
1290              
1291             =back
1292              
1293             Whichever flavor of Data::Presenter::Combo object the user creates, the
1294             module guarantees that each field (column) found in any of the source
1295             databases appears once and once only in the Combo object.
1296              
1297             Data::Presenter is I a database module I, nor is it an interface
1298             to databases in the manner of DBI. It cannot used to enter data into a
1299             database, nor can it be used to modify or delete data. Data::Presenter
1300             operates on I generated from databases and is designed for the user
1301             who:
1302              
1303             =over 4
1304              
1305             =item *
1306              
1307             may not have direct access to a given database;
1308              
1309             =item *
1310              
1311             receives reports from that database generated by another user; but
1312              
1313             =item *
1314              
1315             needs to manipulate and re-output that data in simple, useful ways such as
1316             text files, Perl formats and HTML tables.
1317              
1318             =back
1319              
1320             Data::Presenter is most appropriate in situations where the user either has
1321             no access to (or chooses not to use) commercial desktop database programs such
1322             as I(r) or open source database programs such as I(r).
1323             Data::Presenter's installation and preparation require moderate knowledge of
1324             Perl, but the actual running of Data::Presenter scripts can be delegated to
1325             someone with less knowledge of Perl.
1326              
1327             =head1 DEFINITIONS AND EXAMPLES
1328              
1329             =head2 Definitions
1330              
1331             =head3 Administrator
1332              
1333             The individual in a workplace responsible for the
1334             installation of Data::Presenter on the system or network, analysis of
1335             sources, preparation of Data::Presenter configuration files and preparation
1336             of Data::Presenter subclass packages other than Data::Presenter::Combo and
1337             its subclasses. (I L<"Operator">.)
1338              
1339             =head3 Entry
1340              
1341             A row in the L containing the values of the
1342             fields for one particular item.
1343              
1344             =head3 Field
1345              
1346             A column in the L containing a value for each
1347             entry.
1348              
1349             =head3 Index
1350              
1351             The column in the L whose values uniquely
1352             identify each entry in the source. Also referred to as ''unique ID.'' (In
1353             the current implementation of Data::Presenter, an index must be a strictly
1354             numerical value.)
1355              
1356             =head3 Index Field
1357              
1358             The column in the L containing a unique
1359             value (L<"index">) for each entry.
1360              
1361             =head3 Metadata
1362              
1363             Entries in the Data::Presenter object's data structure which
1364             hold information prepared by the administrator about the data structure and
1365             output parameters.
1366              
1367             In the current version of Data::Presenter, metadata is extracted from the
1368             variables C<@fields>, C<%parameters> and C<$index> found in the configuration
1369             file F. The metadata is first stored in package variables in
1370             the invoking Data::Presenter subclass package and then entered into the
1371             Data::Presenter object as hash entries keyed by C<'fields'>, C<'parameters'>
1372             and C<$index>, respectively. (The word 'options' has also been reserved for
1373             future use as the key of a metadata entry in the object's data structure.)
1374              
1375             =head3 Object's Current Data Structure
1376              
1377             Non-L entries found
1378             in the Data::Presenter object at the point a particular selection, sorting or
1379             output method is called.
1380              
1381             The object's current data structure may be thought of as the result of the
1382             following calculations:
1383              
1384             construct a Data::Presenter::[Package1] object
1385              
1386             less: entries excluded by application of selection criteria found
1387             in C
1388              
1389             less: metadata entries in object keyed by 'fields', 'parameters' or
1390             'fields'
1391              
1392             result: object's current data structure
1393              
1394             =head3 Operator
1395              
1396             The individual in a workplace responsible for running a
1397             Data::Presenter script, including:
1398              
1399             =over 4
1400              
1401             =item *
1402              
1403             selection of sources;
1404              
1405             =item *
1406              
1407             selection of particular entries and fields from the source for presentation
1408             in the output; and
1409              
1410             =item *
1411              
1412             selection of output formats and names of output files. (I
1413             L<"Administrator">.)
1414              
1415             =back
1416              
1417             =head3 Source
1418              
1419             A report, typically saved in the form of a text file, generated
1420             by a database program which presents data in a row-column format. The source
1421             may also contain other information such as page headers and footers and table
1422             headers and footers. Also referred to herein as ''source report,'' ''source
1423             file'' or ''database source report.''
1424              
1425             =head2 Examples
1426              
1427             Sample files are included in the archive file in which this documentation is
1428             found. Three source files, F, F and F,
1429             are included, as are the corresponding Data::Presenter subclass packages
1430             (F, F and F) and configuration files
1431             (F, F and F).
1432              
1433             =head1 USAGE: Administrator
1434              
1435             This section addresses those aspects of the usage of Data::Presenter which
1436             must be implemented by the L:
1437              
1438             =over 4
1439              
1440             =item *
1441              
1442             L of Data::Presenter on the system;
1443              
1444             =item *
1445              
1446             analysis of L;
1447              
1448             =item *
1449              
1450             preparation of Data::Presenter L
1451             File (fields.XXX.data)"> files; and
1452              
1453             =item *
1454              
1455             preparation of Data::Presenter L
1456             Data::Presenter Subclasses"> other than Data::Presenter::Combo and its
1457             subclasses.
1458              
1459             =back
1460              
1461             If Data::Presenter has already been properly configured by your administrator
1462             and you are simply concerned with using Data::Presenter to generate reports,
1463             you may skip ahead to L<"USAGE: Operator">.
1464              
1465             =head2 Installation
1466              
1467             Data::Presenter installs in the same way as other Perl extensions available
1468             from CPAN: either automatically via the CPAN shell or manually with these
1469             commands:
1470              
1471             % gunzip Data-Presenter-1.03.tar.gz
1472             % tar xf Data-Presenter-1.03.tar
1473             % cd Data-Presenter-1.03
1474             % perl Makefile.PL
1475             % make
1476             % make test
1477             % make install
1478              
1479             This will install the following directory tree in your ''site perl''
1480             directory, I a directory such as
1481             F:
1482              
1483             Data/
1484             Presenter.pm
1485             Presenter/
1486             Combo.pm
1487             Combo/
1488             Intersect.pm
1489             Union.pm
1490              
1491              
1492             Once the Administrator has installed Data::Presenter, she must then decide
1493             which location on the network will be used to hold Data::Presenter::[Package1]
1494             subclass packages, where [Package1] is a Data::Presenter subclass in which a
1495             new object will be created. That location could be the F
1496             directory listed above or it could be some other location which users can
1497             access in a Perl program via the C pragma.
1498              
1499             The Administrator must also decide on a location on the network which will be
1500             used to hold the Data::Presenter configuration files -- one for each data
1501             source to be used by Data::Presenter. By convention, each configuration file
1502             is named by some variation on the theme of F.
1503              
1504             Suppose, for instance, that F is the directory
1505             created to hold Data::Presenter-related files accessible to all users.
1506             Suppose, further, that in this business two database reports, I and
1507             I, will be processed via Data::Presenter. The Administrator would
1508             then create a directory tree like this:
1509              
1510             /usr/share/datapresenter/
1511             Data/
1512             Presenter/
1513             Census.pm
1514             Medinsure.pm
1515             config/
1516             fields.census.data
1517             fields.medinsure.data
1518              
1519             The Administrator could also create a directory called F to hold the
1520             source files to be processed with Data::Presenter, and she could also create a
1521             directory called F to hold files created via Data::Presenter -- but
1522             neither of these are strictly necessary.
1523              
1524             =head2 Analysis of Source Files
1525              
1526             Successful use of Data::Presenter assumes that the administrator is able to
1527             analyze a report generated from a database, distinguish key structural
1528             features of such a source report and write Perl code which will extract the
1529             most relevant information from the report. A complete discussion of these
1530             issues is beyond the scope of this documentation. What follows is a taste of
1531             the issues involved.
1532              
1533             Structural features of a database report are likely to include the following:
1534             report headers, page headers, table headers, data entries reporting values
1535             of a variety of fields, page footers and report footers. Of these features,
1536             data entries and table headers are most important from the perspective of
1537             Data::Presenter. The data entries are the data which will actually be
1538             manipulated by Data::Presenter, while table headers will provide the
1539             administrator guidance when writing the configuration file F.
1540             Report and page headers and footers are generally irrelevant and will be
1541             stripped out.
1542              
1543             For example, let us suppose that a portion of a client census looks like
1544             this:
1545              
1546             CLIENTS - AUGUST 1, 2001 - C O N F I D E N T I A L PAGE 1
1547             SHRED WHEN NEW LIST IS RECEIVED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1548             LAST NAME FIRST NAM C. NO BIRTH
1549              
1550             HERNANDEZ HECTOR 456791 1963-07-16
1551             VASQUEZ ADALBERTO 456792 1973-10-02
1552             WASHINGTON ALBERT 906786 1953-03-31
1553              
1554             The first two lines are probably report or page headers and should be
1555             stripped out. The third line consists of table column names and may give
1556             clues as to how F should be written. The fourth line is
1557             blank and should be stripped out. The next three lines constitute actual
1558             rows of data; these will be the focus of Data::Presenter.
1559              
1560             A moderately experienced Perl programmer will look at this report and say,
1561             ''Each row of data can be stored in a Perl array. If each client's 'c. no'
1562             is unique, then it can be used as the key of an entry in a Perl hash where
1563             the entry's value is a reference to the array just mentioned. A hash of
1564             arrays -- I can use Data::Presenter!''
1565              
1566             Our Perl programmer would then say, ''I'll open a filehandle to the source
1567             file and read the file line-by-line into a C loop. I'll write lines
1568             beginning C to bypass the headers and the blank lines.'' For
1569             instance:
1570              
1571             next if (/^CLIENTS/);
1572             next if (/^SHRED/);
1573             next if (/^\s?LAST\sNAME/);
1574             next if (/^$/);
1575              
1576             Our Perl hacker will then say, ''I could try to write regular expressions to
1577             handle the rows of data. But since the data appears to be strictly columnar,
1578             I'll probably be better off using the Perl C function. I'll use the
1579             column headers to suggest names for my variables.'' For instance:
1580              
1581             my ($lastname, $firstname, $cno, $datebirth) =
1582             unpack("x A14 x A10 x A6 x A10", $_);
1583              
1584             Having provided a taste of what to do with the rows of the data structure, we
1585             now turn to an analysis of the columns of the structure.
1586              
1587             =head2 Preparation of Configuration File (F)
1588              
1589             For each data source, the administrator must prepare a configuration file,
1590             typically named as some variation on F.
1591             F consists of three Perl variables:
1592             C<@fields>, C<%parameters> and C<$index>.
1593              
1594             =head3 C<@fields>
1595              
1596             C<@fields> has one element for each column (field) that appears
1597             in the data source. The elements of C<@fields> I appear in exactly the
1598             same order as they appear in the data source. Each element should be a
1599             single Perl word, I, consist solely of letters, numerals or the
1600             underscore character (C<_>).
1601              
1602             In the sample configuration file F included with this
1603             documentation, this variable reads:
1604              
1605             @fields = qw(
1606             lastname firstname cno unit ward dateadmission datebirth
1607             );
1608              
1609             In another sample configuration file, F, this variable
1610             reads:
1611              
1612             @fields = qw(lastname firstname cno stateid medicare medicaid);
1613              
1614             =head3 C<%parameters>
1615              
1616             C<%parameters> is a bit trickier. There must be one entry
1617             in C<%parameters> for each element in C<@fields>. Hence, there is one entry
1618             in C<%parameters> for each column (field) in the data source. However, the
1619             keys of C<%parameters> are spelled C<$fields[0]>, C<$fields[1]>, and so on
1620             through the highest index number in C<@fields> (which is 1 less than the
1621             number of elements in C<@fields>). Using the example above, we can begin to
1622             construct C<%parameters> as follows:
1623              
1624             %parameters = (
1625             $fields[0] =>
1626             $fields[1] =>
1627             $fields[2] =>
1628             $fields[3] =>
1629             $fields[4] =>
1630             $fields[5] =>
1631             $fields[6] =>
1632             );
1633              
1634             The value for each entry in C<%parameters> consists of an array of 4 elements
1635             specified as follows:
1636              
1637             =over 4
1638              
1639             =item Element 0
1640              
1641             A positive integer specifying the maximum number of characters which may be
1642             displayed in any output format for the given column (field). In the example
1643             above, we will specify that column 'lastname' (C<$fields[0]>) may have a
1644             maximum of 14 characters.
1645              
1646             $fields[0] => [14,
1647              
1648             =item Element 1
1649              
1650             An upper-case letter 'U' or 'D' (for 'Up' or 'Down') enclosed in single
1651             quotation marks indicating whether the given column should be sorted in
1652             ascending or descending order. In the example above, 'lastname' sorts in
1653             ascending order.
1654              
1655             $fields[0] => [14, 'U',
1656              
1657             =item Element 2
1658              
1659             A lower-case letter 'a', 'n' or 's' enclosed in single quotation marks
1660             indicating whether the given column should be sorted alphabetically
1661             (case-insensitive), numerically or ASCII-betically (case-sensitive). In the
1662             example above, 'lastname' sorts in alphabetical order. (Data::Presenter
1663             I does not yet have a facility for sorting in date or time order. If
1664             dates are entered as pure numerals in 'MMDD' order, they may be sorted
1665             numerically. If they are entered in the MySQL standard format '
1666             YY-MM-DD', they may be sorted alphabetically.)
1667              
1668             $fields[0] => [14, 'U', 'a',
1669              
1670             =item Element 3
1671              
1672             A string enclosed in single quotation marks to be used as a column header
1673             when the data is outputted in some table-like format such as a Perl format
1674             with a header or an HTML table. The administrator may choose to use exactly
1675             the same words here that were used in C<@fields>, but a more natural language
1676             string is probably preferable. In the example above, the first column will
1677             carry the title 'Last Name' in any output.
1678              
1679             $fields[0] => [14, 'U', 'a', 'Last Name'],
1680              
1681             =back
1682              
1683             Using the same example as previously, we can now complete C<%parameters> as:
1684              
1685             %parameters = (
1686             $fields[0] => [14, 'U', 'a', 'Last Name'],
1687             $fields[1] => [10, 'U', 'a', 'First Name'],
1688             $fields[2] => [ 7, 'U', 'n', 'C No.'],
1689             $fields[3] => [ 6, 'U', 'a', 'Unit'],
1690             $fields[4] => [ 4, 'U', 'n', 'Ward'],
1691             $fields[5] => [10, 'U', 'a', 'Date of Admission'],
1692             $fields[6] => [10, 'U', 'a', 'Date of Birth'],
1693             );
1694              
1695             =head3 C<$index>
1696              
1697             C<$index> is the simplest element of I. It is the
1698             array index for the entry in C<@fields> which describes the field in the data
1699             source whose values uniquely identify each entry in the source. If, in the
1700             example above, C<'cno'> is the L for the data in
1701             I, then C<$index> is C<2>. (Remember that Perl starts counting
1702             array elements with C<0>.)
1703              
1704             =head2 Preparation of Data::Presenter Subclasses
1705              
1706             F, F,
1707             F and F
1708             are ready to use ''as is.'' They require no further modification by the
1709             administrator. However, each report from which the operator draws data needs
1710             to have a package subclassed beneath Data::Presenter and written specifically
1711             for that report by the administrator.
1712              
1713             Indeed, B from Data::Presenter.
1714             All objects are constructed from subclasses of Data::Presenter.>
1715              
1716             Hence:
1717              
1718             $dp1 = Data::Presenter->new( # INCORRECT
1719             $source, \@fields, \%parameters, $index);
1720              
1721             $dp1 = Data::Presenter::[Package1]->new( # CORRECT
1722             $source, \@fields, \%parameters, $index);
1723              
1724             Data::Presenter::[Package1], however, does not contain a C method. It
1725             inherits Data::Presenter's C method -- which then turns around and
1726             delegates the task of populating the object with data to
1727             Data::Presenter::[Package1]'s C<_init()> method!
1728              
1729             This C<_init()> method must be customized by the administrator to properly
1730             handle the specific features of each source file. This requires that the
1731             administrator be able to write a Perl script to 'clean up' the source file so
1732             that only lines containing meaningful data are written to the Data::Presenter
1733             object. (See L<"Analysis of Source Files"> above.) With that in mind, a
1734             Data::Presenter::[Package1] package must always include the following
1735             methods:
1736              
1737             =over 4
1738              
1739             =item * C<_init()>
1740              
1741             This method is called from within the constructor and is used to populate the
1742             hash which is blessed into the new object. It opens a filehandle to the
1743             source file and typically reads that source file line-by-line via a Perl
1744             C loop. Perl techniques and functions such as regular expressions,
1745             C and C are used to populate a hash of arrays and to strip out
1746             lines in the data source not needed in the object. Should the administrator
1747             need to ''munge'' any of the incoming data so that it appears in a uniform
1748             format (I, '2001-07-02' rather than '7/2/2001' or '07/02/2001'), the
1749             administrator should write appropriate code within C<_init()> or in a separate
1750             module imported into the main package. Each element of each array used to
1751             store a data record must have a defined value. C is not permitted;
1752             assign an empty string to the element instead. A reference to this hash of
1753             arrays is returned to the constructor, which blesses it into the object.
1754              
1755             =item * C<_extract_rows>
1756              
1757             This method is called from within the Data::Presenter C method.
1758             In much the same manner as C<_init()>, it permits the administrator to
1759             ''munge'' operator-typed data to achieve a uniform format.
1760              
1761             =back
1762              
1763             The packages F and F
1764             found in the F directory in this distribution provide examples of
1765             C<_init()> and C<_extract_rows>. Search for the lines of code which read:
1766              
1767             # DATA MUNGING STARTS HERE
1768             # DATA MUNGING ENDS HERE
1769              
1770             Here is a simple example of data munging. In the sample configuration file
1771             F, all elements of C<@fields> are entered entirely in
1772             lower-case. Hence, it would be advisable to transform the operator-specified
1773             content of C<$column> to all lower-case so that the program does not fail
1774             simply because an operator types an upper-case letter. See C<_extract_rows()>
1775             in the Data::Presenter::Census package included with this documentation for
1776             an example.
1777              
1778             Sample file F contains an example of a subroutine
1779             written to clean up repetitive coding within the data munging section.
1780             Search for C.
1781              
1782             =head1 USAGE: Operator
1783              
1784             Once the administrator has installed Data::Presenter and completed the
1785             preparation of configuration files and Data::Presenter subclass packages, the
1786             administrator may turn over to the operator the job of selecting particular
1787             source files, output formats and particular entries and fields from within
1788             the source files.
1789              
1790             =head2 Construction of a Data::Presenter Object
1791              
1792             =head3 Declarations
1793              
1794             Using the hospital census example included with this documentation, the
1795             operator would construct a Data::Presenter::Census object with the following
1796             code:
1797              
1798             use Data::Presenter;
1799             use lib ("/usr/share/datapresenter");
1800             use Data::Presenter::Census;
1801              
1802             our @fields = ();
1803             our %parameters = ();
1804             our $index = q{};
1805              
1806             my $sourcefile = 'census.txt';
1807             my $configdir = "/usr/share/datapresenter";
1808             my $configfile = "$configdir/fields.census.data";
1809              
1810             do $configfile;
1811              
1812             =head3 C
1813              
1814             my $dp1 = Data::Presenter::Census->new(
1815             $sourcefile, \@fields, \%parameters, $index);
1816              
1817             =head2 Methods to Report on the Data::Presenter Object Itself
1818              
1819             =head3 C
1820              
1821             Returns the current number of data entries in the
1822             specified Data::Presenter object. This number does I include those
1823             elements in the object whose keys are reserved words. This method takes no
1824             arguments and returns one numerical scalar.
1825              
1826             my $data_count = $dp1->get_data_count();
1827             print 'Data count is now: ', $data_count, "\n";
1828              
1829             =head3 C
1830              
1831             Prints the current data count preceded by ''Current
1832             data count: ''. This number does I include those elements in the
1833             object whose keys are reserved words. This method takes no arguments and
1834             returns no values.
1835              
1836             $dp1->print_data_count();
1837              
1838             =head3 C
1839              
1840             Returns a reference to an array whose elements are an
1841             ASCII-betically sorted list of keys to the hash blessed into the
1842             Data::Presenter::[Package1] object. This list does not include those
1843             elements whose keys are reserved words. This method takes no arguments and
1844             returns only the array reference described.
1845              
1846             my $keysref = $dp1->get_keys();
1847             print "Current data points are: @$keysref\n";
1848              
1849             =head3 C
1850              
1851             Returns a reference to a hash whose elements are
1852             key-value pairs where the key is the key of an element blessed into the
1853             Data::Presenter::[Package1] object and the value is 1, indicating that the
1854             key has been seen (a 'seen-hash'). This list does not include those elements
1855             whose keys are reserved words. This method takes no arguments and returns
1856             only the hash reference described.
1857              
1858             my $seenref = $dp1->get_keys_seen();
1859             print "Current data points are: ";
1860             print "$_ " foreach (sort keys %{$seenref});
1861             print "\n";
1862              
1863             =head3 C
1864              
1865             Takes as argument a single string which is the name of one of the fields
1866             listed in C<@fields> in the configuration file. Returns a reference to a hash
1867             whose elements are keyed by the entries for that field in the data source and
1868             whose values are the number of times each entry was seen in the data.
1869              
1870             For example, if the data consisted of this:
1871              
1872             HERNANDEZ HECTOR 1963-08-01 456791
1873             VASQUEZ ADALBERTO 1973-08-17 786792
1874             VASQUEZ ALBERTO 1953-02-28 906786
1875              
1876             where the left-most column was described in C<@fields> as C, then:
1877              
1878             $seenref = $dp1->seen_one_column('lastname');
1879              
1880             and C<$seenref> would hold:
1881              
1882             {
1883             HERNANDEZ => 1,
1884             VASQUEZ => 2,
1885             }
1886              
1887             =head2 Data::Presenter Selection, Sorting and Output Methods
1888              
1889             =head3 C
1890              
1891             C enables the operator to establish criteria
1892             by which specific entries from the data can be selected for output. It does
1893             so I by creating a new object but by striking out entries in the
1894             L which do
1895             not meet the selection criteria.
1896              
1897             If the operator were using Perl as an interface to a true database program,
1898             selection of entries would most likely be handled by a module such as DBI and
1899             an SQL-like query. In that case, it would be possible to write complex
1900             selection queries which operate on more than one field at a time such as:
1901              
1902             select rows where 'datebirth' is before 01/01/1960
1903             AND 'lastname' equals 'Vasquez'
1904             # (NOTE: This is generic code,
1905             # not true Perl or Perl DBI code.)
1906              
1907             Complex selection queries are not yet possible in Data::Presenter. However,
1908             you could accomplish much the same objective with a series of simple
1909             selection queries that operate on only one field at a time,
1910              
1911             select rows where 'datebirth" is before 01/01/1960
1912              
1913             then
1914              
1915             select rows where 'lastname' equals 'Vasquez'
1916              
1917             each of which narrows the selection criteria.
1918              
1919             How do we accomplish this within Data::Presenter? For each selection query,
1920             the operator must define 3 variables: C<$column>, C<$relation> and
1921             C<@choices>. These variables are passed to C, which in turn
1922             passes them to certain internal subroutines where their values are
1923             manipulated as follows.
1924              
1925             =over 4
1926              
1927             =item * C<$column>
1928              
1929             C<$column> must be an element of L|"@fields"> found in the
1930             L.
1931              
1932             =item * C<$relation>
1933              
1934             C<$relation> expresses the verb part of the selection query, I
1935             relations such as C, C, C=>, C and so
1936             forth. In an attempt to add natural language flexibility to the selection
1937             query, Data::Presenter permits the operator to enter a wide variety of
1938             mathematical and English expressions here:
1939              
1940             =over 4
1941              
1942             =item * equality
1943              
1944             'eq', 'equals', 'is', 'is equal to', 'is a member of',
1945             'is part of', '=', '=='
1946              
1947             =item * non-equality
1948              
1949             'is', 'is not', 'is not equal to', 'is not a member of',
1950             'is not part of', 'is less than or greater than',
1951             'is less than or more than', 'is greater than or less than',
1952             'is more than or less than', 'does not equal', 'not',
1953             'not equal to ', 'not equals', '!=', '! =', '!==', '! =='
1954              
1955             =item * less than
1956              
1957             '<', 'lt', 'is less than', 'is fewer than', 'before'
1958              
1959             =item * greater than
1960              
1961             '>', 'gt', 'is more than', 'is greater than', 'after'
1962              
1963             =item * less than or equal to
1964              
1965             '<=', 'le', 'is less than or equal to',
1966             'is fewer than or equal to', 'on or before', 'before or on'
1967              
1968             =item * greater than or equal to
1969              
1970             '>=', 'ge', 'is more than or equal to', 'is greater than or equal to',
1971             'on or after', 'after or on'
1972              
1973             =back
1974              
1975             As long as the operator selects a string from the category desired,
1976             Data::Presenter will convert it internally in an appropriate manner.
1977              
1978             =item * C<@choices>
1979              
1980             If the relationship being tested is one of equality or non-equality, then the
1981             operator may enter more than one value here, any one of which may satisfy the
1982             selection criterion.
1983              
1984             my ($column, $relation, @choices);
1985              
1986             $column = 'lastname';
1987             $relation = 'is';
1988             @choices = ('Smith', 'Jones');
1989             $dp1->select_rows($column, $relation, \@choices);
1990              
1991             If, however, the relationship being tested is one of 'less than', 'greater
1992             than', etc., then the operator should enter only one value, as the value is
1993             establishing a limit above or below which the selection criterion will not be
1994             met.
1995              
1996             $column = 'datebirth';
1997             $relation = 'before';
1998             @choices = ('01/01/1970');
1999             $dp1->select_rows($column, $relation, \@choices);
2000              
2001             =back
2002              
2003             =head3 C
2004              
2005             C takes only 1 argument: a reference
2006             to an array consisting of the fields the operator wishes to present in the
2007             final output, listed in the order in which those fields should be sorted.
2008             All elements of this array must be elements in C<@fields>. B
2009             must always be included as one of the columns selected,> though it may be
2010             placed last if it is not intrinsically important in the final output.
2011             C returns a reference to a hash of appropriately sorted data
2012             which will be used as input to Data::Presenter methods such as
2013             C, C and C.
2014              
2015             To illustrate:
2016              
2017             my @columns_selected = ('lastname', 'firstname', 'datebirth', 'cno');
2018             $sorted_data = $dp1->sort_by_column(\@columns_selected);
2019              
2020             Suppose that the operator fails to include the index column in
2021             C<@columns_selected>. This risks having two or more identical data entries,
2022             only the last of which would appear in the final output. As a safety
2023             precaution, C throws a warning and places duplicate entries
2024             in a text file called F.
2025              
2026             Note: If you want your output to report only selected entries from the
2027             source, and if you want to apply one of the complex Data::Presenter output
2028             methods which require application of C, call C
2029             I calling C. Otherwise your report may contain
2030             blank lines.
2031              
2032             =head3 C
2033              
2034             C prints to standard output (generally, the computer
2035             monitor) a semicolon-delimited display of all entries in the object's
2036             current data structure. It takes no arguments and returns no values.
2037              
2038             $dp1->print_to_screen();
2039              
2040             A typical line of output will look something like:
2041              
2042             VASQUEZ;JORGE;456787;LAVER;0105;1986-01-17;1956-01-13;
2043              
2044             =head3 C
2045              
2046             C prints to an operator-specified file a
2047             semicolon-delimited display of all entries in the object's current data
2048             structure. It takes 1 argument -- the user-specified output file -- and
2049             returns no values.
2050              
2051             $outputfile = 'census01.txt';
2052             $dp1->print_to_file($outputfile);
2053              
2054             A typical line of output will look exactly like that produced by
2055             L|"print_to_screen()">.
2056              
2057             =head3 C
2058              
2059             C, like C,
2060             prints to an operator-specified file. C allows the
2061             operator to specify the character pattern which will be used to delimit
2062             display of all entries in the object's current data structure. It does not
2063             print the delimiter after the final field in a particular data record. It
2064             takes 2 arguments -- the user-specified output file and the character pattern
2065             to be used as delimiter -- and returns no values.
2066              
2067             $outputfile = 'delimited01.txt';
2068             $delimiter = '|||';
2069             $dp1->print_with_delimiter($outputfile, $delimiter);
2070              
2071             The file created C is designed to be used as an input
2072             to functions such as 'Convert text to tabs' or 'Convert text to table' found
2073             in commercial word processing programs. Such functions require delimiter
2074             characters in the input. A typical line of output will look something like:
2075              
2076             VASQUEZ|||JORGE|||456787|||LAVER|||0105|||1986-01-17|||1956-01-13
2077              
2078             =head3 C
2079              
2080             C prints to an operator-specified file each
2081             entry in the object's current data structure, sorted by the index and
2082             explicitly naming each field name/field value pair. It takes 1 argument --
2083             the user-specified output file -- and returns no values.
2084              
2085             $outputfile = 'report01.txt';
2086             $dp1->full_report($outputfile);
2087              
2088             The output for a given entry will look something like:
2089              
2090             456787
2091             lastname VASQUEZ
2092             firstname JORGE
2093             cno 456787
2094             unit LAVER
2095             ward 0105
2096             dateadmission 1986-01-17
2097             datebirth 1956-01-13
2098              
2099             =head3 C
2100              
2101             C writes data via Perl's C function
2102             -- the function which internally powers Perl formats -- to an
2103             operator-specified file. C takes a list of 3 key-value pairs:
2104              
2105             $dp1->writeformat(
2106             sorted => $sorted_data,
2107             columns => \@columns_selected,
2108             file => $outputfile,
2109             );
2110              
2111             =over 4
2112              
2113             =item * C
2114              
2115             The value of C is a hash reference which is the return value of
2116             C. Hence, C can only be called once
2117             C has been called.
2118              
2119             =item * C
2120              
2121             The value of C is a reference to the array of fields in the data
2122             source selected for presentation in the output file. It is the same variable
2123             which is used as the argument to C.
2124              
2125             =item * C
2126              
2127             The value of C is the name of a file arbitrarily selected by the
2128             operator to hold the output of C.
2129              
2130             =back
2131              
2132             Using the ''census'' example from above, the overall sequence of code needed
2133             to use C would be:
2134              
2135             @columns_selected = ('lastname', 'firstname', 'datebirth', 'cno');
2136             $sorted_data = $dp1->sort_by_column(\@columns_selected);
2137              
2138             $dp1->writeformat(
2139             sorted => $sorted_data,
2140             columns => \@columns_selected,
2141             file => $outputfile,
2142             );
2143              
2144             The result of the above call would be a file named F containing:
2145              
2146             HERNANDEZ HECTOR 1963-08-01 456791
2147             VASQUEZ ADALBERTO 1973-08-17 786792
2148             VASQUEZ ALBERTO 1953-02-28 906786
2149              
2150             The columnar appearance of the data is governed by choices made by the
2151             administrator within the configuration file (here, within
2152             F). The choice of columns themselves is controlled by
2153             the operator via C<\@columns_selected>.
2154              
2155             =head3 C
2156              
2157             C writes data via
2158             Perl formats to an operator-specified file and writes a Perl format header to
2159             that file as well. C takes a list of 4 key-value
2160             pairs. Three of these pairs are the same as in C; the fourth
2161             is:
2162              
2163             =over 4
2164              
2165             =item * C </td> </tr> <tr> <td class="h" > <a name="2166">2166</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2167">2167</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> title => $title, </td> </tr> <tr> <td class="h" > <a name="2168">2168</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2169">2169</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<title> holds text chosen by the operator. </td> </tr> <tr> <td class="h" > <a name="2170">2170</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2171">2171</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =back </td> </tr> <tr> <td class="h" > <a name="2172">2172</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2173">2173</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The complete call to C<writeformat_plus_header> looks like this: </td> </tr> <tr> <td class="h" > <a name="2174">2174</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2175">2175</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> @columns_selected = ('lastname', 'firstname', 'datebirth', 'cno'); </td> </tr> <tr> <td class="h" > <a name="2176">2176</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $sorted_data = $dp1->sort_by_column(\@columns_selected); </td> </tr> <tr> <td class="h" > <a name="2177">2177</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2178">2178</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $dp1->writeformat_plus_header( </td> </tr> <tr> <td class="h" > <a name="2179">2179</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> sorted => $sorted_data, </td> </tr> <tr> <td class="h" > <a name="2180">2180</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> columns => \@columns_selected, </td> </tr> <tr> <td class="h" > <a name="2181">2181</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> file => $outputfile, </td> </tr> <tr> <td class="h" > <a name="2182">2182</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> title => $title, </td> </tr> <tr> <td class="h" > <a name="2183">2183</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2184">2184</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2185">2185</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> and will produce a header and formatted data like this: </td> </tr> <tr> <td class="h" > <a name="2186">2186</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2187">2187</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Hospital Census Report </td> </tr> <tr> <td class="h" > <a name="2188">2188</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2189">2189</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Date Date of </td> </tr> <tr> <td class="h" > <a name="2190">2190</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Unit Ward Last Name First Name of Birth Admission C No. </td> </tr> <tr> <td class="h" > <a name="2191">2191</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ------------------------------------------------------------------ </td> </tr> <tr> <td class="h" > <a name="2192">2192</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> LAVER 0105 VASQUEZ JORGE 1956-01-13 1986-01-17 456787 </td> </tr> <tr> <td class="h" > <a name="2193">2193</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> LAVER 0107 VASQUEZ LEONARDO 1970-15-23 1990-08-23 456788 </td> </tr> <tr> <td class="h" > <a name="2194">2194</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> SAMSON 0209 VASQUEZ JOAQUIN 1970-03-25 1990-11-14 456789 </td> </tr> <tr> <td class="h" > <a name="2195">2195</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2196">2196</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The wording of the column headers is governed by choices made by the </td> </tr> <tr> <td class="h" > <a name="2197">2197</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> administrator within the configuration file (here, within </td> </tr> <tr> <td class="h" > <a name="2198">2198</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> F<fields.census.data>). If a particular word in a column header is too long </td> </tr> <tr> <td class="h" > <a name="2199">2199</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> to fit in the space allocated, it will be truncated. </td> </tr> <tr> <td class="h" > <a name="2200">2200</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2201">2201</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head3 C<writeformat_with_reprocessing()> </td> </tr> <tr> <td class="h" > <a name="2202">2202</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2203">2203</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeformat_with_reprocessing()> is an </td> </tr> <tr> <td class="h" > <a name="2204">2204</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> advanced application of Data::Presenter and the reader may wish to skip this </td> </tr> <tr> <td class="h" > <a name="2205">2205</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> section until other parts of the module have been mastered. </td> </tr> <tr> <td class="h" > <a name="2206">2206</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2207">2207</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeformat_with_reprocessing()> permits a sophisticated administrator to </td> </tr> <tr> <td class="h" > <a name="2208">2208</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> activate ''last minute'' substitutions in the strings printed out from the </td> </tr> <tr> <td class="h" > <a name="2209">2209</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> format accumulator variable C<$^A>. Suppose, for example, that a school </td> </tr> <tr> <td class="h" > <a name="2210">2210</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> administrator faced the problem of scheduling classes in different classrooms </td> </tr> <tr> <td class="h" > <a name="2211">2211</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> and in various time slots. Suppose further that, for ease of programming or </td> </tr> <tr> <td class="h" > <a name="2212">2212</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> data entry, the time slots were identified by chronologically sequential </td> </tr> <tr> <td class="h" > <a name="2213">2213</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> numbers and that instructors were identified by a unique ID built up from </td> </tr> <tr> <td class="h" > <a name="2214">2214</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> their first and last names. Applying an ordinary C<writeformat()> to such </td> </tr> <tr> <td class="h" > <a name="2215">2215</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> data might show output like this </td> </tr> <tr> <td class="h" > <a name="2216">2216</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2217">2217</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 11 Arithmetic Jones 4044 4044_11 </td> </tr> <tr> <td class="h" > <a name="2218">2218</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 11 Language Studies WilsonT 4054 4054_11 </td> </tr> <tr> <td class="h" > <a name="2219">2219</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 12 Bible Study Eliade 4068 4068_12 </td> </tr> <tr> <td class="h" > <a name="2220">2220</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 12 Introduction to Computers Knuth 4086 4086_12 </td> </tr> <tr> <td class="h" > <a name="2221">2221</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 13 Psychology Adler 4077 4077_13 </td> </tr> <tr> <td class="h" > <a name="2222">2222</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 13 Social Science JonesT 4044 4044_13 </td> </tr> <tr> <td class="h" > <a name="2223">2223</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 51 World History Wells 4052 4052_51 </td> </tr> <tr> <td class="h" > <a name="2224">2224</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 51 Music Appreciation WilsonW 4044 4044_51 </td> </tr> <tr> <td class="h" > <a name="2225">2225</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2226">2226</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> where C<11> mapped to 'Monday, 9:00 am', C<12> to 'Monday, 10:00 am', C<51> </td> </tr> <tr> <td class="h" > <a name="2227">2227</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> to 'Friday, 9:00 am' and so forth and where the fields underlying this output </td> </tr> <tr> <td class="h" > <a name="2228">2228</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> were 'timeslot', 'classname', 'instructor', 'room' and 'sessionID'. While </td> </tr> <tr> <td class="h" > <a name="2229">2229</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> this presentation is useful, a client might wish to have the time slots and </td> </tr> <tr> <td class="h" > <a name="2230">2230</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> instructor IDs decoded for more readable output: </td> </tr> <tr> <td class="h" > <a name="2231">2231</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2232">2232</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 9:00 Arithmetic E Jones 4044 4044_11 </td> </tr> <tr> <td class="h" > <a name="2233">2233</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 9:00 Language Studies T Wilson 4054 4054_11 </td> </tr> <tr> <td class="h" > <a name="2234">2234</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 10:00 Bible Study M Eliade 4068 4068_12 </td> </tr> <tr> <td class="h" > <a name="2235">2235</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 10:00 Introduction to Computers D Knuth 4086 4086_12 </td> </tr> <tr> <td class="h" > <a name="2236">2236</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 11:00 Psychology A Adler 4077 4077_13 </td> </tr> <tr> <td class="h" > <a name="2237">2237</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 11:00 Social Science T Jones 4044 4044_13 </td> </tr> <tr> <td class="h" > <a name="2238">2238</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Friday, 9:00 World History H Wells 4052 4052_51 </td> </tr> <tr> <td class="h" > <a name="2239">2239</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Friday, 9:00 Music Appreciation W Wilson 4044 4044_51 </td> </tr> <tr> <td class="h" > <a name="2240">2240</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2241">2241</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Time slots coded with chronologically sequential numbers can be ordered to </td> </tr> <tr> <td class="h" > <a name="2242">2242</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> sort numerically in the C<%parameters> established in the </td> </tr> <tr> <td class="h" > <a name="2243">2243</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> F<fields.[package1].data> file corresponding to a particular </td> </tr> <tr> <td class="h" > <a name="2244">2244</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Data::Presenter::[package1]. Their human-language equivalents, however, will </td> </tr> <tr> <td class="h" > <a name="2245">2245</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> I<not> sort properly, as, for example, 'Friday' comes before 'Monday' in an </td> </tr> <tr> <td class="h" > <a name="2246">2246</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> alphabetical or ASCII-betical sort. Clearly, it would be desirable to </td> </tr> <tr> <td class="h" > <a name="2247">2247</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> establish the sorting order by relying on the chronologically sequential time </td> </tr> <tr> <td class="h" > <a name="2248">2248</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> slots and yet have the printed output reflect more human-readable days of the </td> </tr> <tr> <td class="h" > <a name="2249">2249</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> week and times. Analogously, for the instructor we might wish to display the </td> </tr> <tr> <td class="h" > <a name="2250">2250</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> first initial and last name in our printed output rather than his/her ID </td> </tr> <tr> <td class="h" > <a name="2251">2251</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> code. </td> </tr> <tr> <td class="h" > <a name="2252">2252</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2253">2253</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The order in which data records appear in output is determined by </td> </tr> <tr> <td class="h" > <a name="2254">2254</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<sort_by_column()> I<before> C<writeformat()> is called. How can we preserve </td> </tr> <tr> <td class="h" > <a name="2255">2255</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> this order in the final output? </td> </tr> <tr> <td class="h" > <a name="2256">2256</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2257">2257</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Answer: After we have stored a given formed line in C<$^A>, we I<reprocess> </td> </tr> <tr> <td class="h" > <a name="2258">2258</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> that line by calling an internal subroutine defined in the invoking class, </td> </tr> <tr> <td class="h" > <a name="2259">2259</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<Data::Presenter::[package1]::_reprocessor()>, which tells Perl to splice out </td> </tr> <tr> <td class="h" > <a name="2260">2260</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> certain portions of the formed line and substitute more human-readable copy. </td> </tr> <tr> <td class="h" > <a name="2261">2261</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The information needed to make C<_reprocessor()> work comes from two places. </td> </tr> <tr> <td class="h" > <a name="2262">2262</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2263">2263</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> First, from a hash passed by reference as an argument to </td> </tr> <tr> <td class="h" > <a name="2264">2264</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeformat_with_reprocessing()>. C<writeformat_with_reprocessing()> takes </td> </tr> <tr> <td class="h" > <a name="2265">2265</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> a list of four key-value pairs, the first three of which are the same as those </td> </tr> <tr> <td class="h" > <a name="2266">2266</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> passed to C<writeformat()>. The fourth key-value pair to </td> </tr> <tr> <td class="h" > <a name="2267">2267</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeformat_with_reprocessing()> is a reference to a hash whose keys are </td> </tr> <tr> <td class="h" > <a name="2268">2268</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the names of the fields in the data records where we wish to make </td> </tr> <tr> <td class="h" > <a name="2269">2269</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> substitutions and whose corresponding values are the number of characters </td> </tr> <tr> <td class="h" > <a name="2270">2270</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the field will be allocated I<after> substitution. The call to </td> </tr> <tr> <td class="h" > <a name="2271">2271</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeformat_with_reprocessing()> would therefore look like this: </td> </tr> <tr> <td class="h" > <a name="2272">2272</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2273">2273</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %reprocessing_info = ( </td> </tr> <tr> <td class="h" > <a name="2274">2274</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> timeslot => 17, </td> </tr> <tr> <td class="h" > <a name="2275">2275</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> instructor => 15, </td> </tr> <tr> <td class="h" > <a name="2276">2276</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2277">2277</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2278">2278</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $dp1->writeformat_with_reprocessing( </td> </tr> <tr> <td class="h" > <a name="2279">2279</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> sorted => $sorted_data, </td> </tr> <tr> <td class="h" > <a name="2280">2280</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> columns => \@columns_selected, </td> </tr> <tr> <td class="h" > <a name="2281">2281</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> file => $outputfile, </td> </tr> <tr> <td class="h" > <a name="2282">2282</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> reprocess => \%reprocessing_info, </td> </tr> <tr> <td class="h" > <a name="2283">2283</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2284">2284</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2285">2285</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Second, C<writeformat_with_reprocessing()> takes advantage of the fact that </td> </tr> <tr> <td class="h" > <a name="2286">2286</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Data::Presenter's package global hash C<%reserved> contains four keys -- </td> </tr> <tr> <td class="h" > <a name="2287">2287</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<fields>, C<parameters>, C<index> and C<options> -- only the first </td> </tr> <tr> <td class="h" > <a name="2288">2288</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> three of which are used in Data::Presenter's constructor or sorting methods. </td> </tr> <tr> <td class="h" > <a name="2289">2289</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Early in the development of Data::Presenter the keyword C<options> was </td> </tr> <tr> <td class="h" > <a name="2290">2290</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> deliberately left unused so as to be available for future use. </td> </tr> <tr> <td class="h" > <a name="2291">2291</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2292">2292</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The sophisticated administrator can make use of the C<options> key to store </td> </tr> <tr> <td class="h" > <a name="2293">2293</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> metadata in a variety of ways. In writing </td> </tr> <tr> <td class="h" > <a name="2294">2294</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<Data::Presenter::[package1]::_init()>, the administrator prepares the way </td> </tr> <tr> <td class="h" > <a name="2295">2295</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> for last-minute reprocessing by creating an C<options> key in the hash to </td> </tr> <tr> <td class="h" > <a name="2296">2296</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> be blessed into the C<Data::Presenter::[package1]()> object. The value </td> </tr> <tr> <td class="h" > <a name="2297">2297</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> corresponding to the key C<options> is itself a hash with two elements </td> </tr> <tr> <td class="h" > <a name="2298">2298</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> keyed by C<subs> and C<sources>. If C<$dp1> is the object and C<%data> </td> </tr> <tr> <td class="h" > <a name="2299">2299</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> is the hash blessed into the object, then we are looking at these two </td> </tr> <tr> <td class="h" > <a name="2300">2300</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> elements: </td> </tr> <tr> <td class="h" > <a name="2301">2301</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2302">2302</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $data{options}{subs} </td> </tr> <tr> <td class="h" > <a name="2303">2303</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $data{options}{sources} </td> </tr> <tr> <td class="h" > <a name="2304">2304</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2305">2305</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The values corresponding to these two keys are references to yet more hashes. </td> </tr> <tr> <td class="h" > <a name="2306">2306</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The hash which is the value for C<$data{options}{subs}> hash keys whose </td> </tr> <tr> <td class="h" > <a name="2307">2307</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> elements are the name of subroutines, each of which is built up from the </td> </tr> <tr> <td class="h" > <a name="2308">2308</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> string C<reprocess_> concatenated with the name of the field to be </td> </tr> <tr> <td class="h" > <a name="2309">2309</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> reprocessed, I<e.g.> </td> </tr> <tr> <td class="h" > <a name="2310">2310</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2311">2311</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $data{options}{subs} = { </td> </tr> <tr> <td class="h" > <a name="2312">2312</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> reprocess_timeslot => 1, </td> </tr> <tr> <td class="h" > <a name="2313">2313</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> reprocess_instructor => 1, </td> </tr> <tr> <td class="h" > <a name="2314">2314</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> }; </td> </tr> <tr> <td class="h" > <a name="2315">2315</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2316">2316</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> These field-specific internal reprocessing subroutines may be defined by the </td> </tr> <tr> <td class="h" > <a name="2317">2317</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> administrator in C<Data::Presenter::[package1]()> or they may be imported from </td> </tr> <tr> <td class="h" > <a name="2318">2318</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> some other module. C<writeformat_with_reprocessing()> verifies that these </td> </tr> <tr> <td class="h" > <a name="2319">2319</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> subroutines are actually present in C<Data::Presenter::[package1]()> </td> </tr> <tr> <td class="h" > <a name="2320">2320</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> regardless of where they were originally found. </td> </tr> <tr> <td class="h" > <a name="2321">2321</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2322">2322</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> What about C<$data{options}{sources}>? This location stores all the </td> </tr> <tr> <td class="h" > <a name="2323">2323</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> original data from which substitutions are made. Example: </td> </tr> <tr> <td class="h" > <a name="2324">2324</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2325">2325</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $data{options}{sources} = { </td> </tr> <tr> <td class="h" > <a name="2326">2326</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> timeslot => { </td> </tr> <tr> <td class="h" > <a name="2327">2327</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 11 => ['Monday', '9:00 am' ], </td> </tr> <tr> <td class="h" > <a name="2328">2328</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 12 => ['Monday', '10:00 am' ], </td> </tr> <tr> <td class="h" > <a name="2329">2329</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 13 => ['Monday', '11:00 am' ], </td> </tr> <tr> <td class="h" > <a name="2330">2330</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 51 => ['Friday', '9:00 am' ], </td> </tr> <tr> <td class="h" > <a name="2331">2331</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> }, </td> </tr> <tr> <td class="h" > <a name="2332">2332</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> instructor => { </td> </tr> <tr> <td class="h" > <a name="2333">2333</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 'Jones' => ['Jones', 'E' ], </td> </tr> <tr> <td class="h" > <a name="2334">2334</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 'WilsonT' => ['Wilson', 'T' ], </td> </tr> <tr> <td class="h" > <a name="2335">2335</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 'Eliade' => ['Eliade', 'M' ], </td> </tr> <tr> <td class="h" > <a name="2336">2336</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 'Knuth' => ['Knuth', 'D' ], </td> </tr> <tr> <td class="h" > <a name="2337">2337</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 'Adler' => ['Adler', 'A' ], </td> </tr> <tr> <td class="h" > <a name="2338">2338</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 'JonesT' => ['Jones', 'T' ], </td> </tr> <tr> <td class="h" > <a name="2339">2339</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 'Wells' => ['Wells', 'H' ], </td> </tr> <tr> <td class="h" > <a name="2340">2340</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 'WilsonW' => ['Wilson', 'W' ], </td> </tr> <tr> <td class="h" > <a name="2341">2341</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> } </td> </tr> <tr> <td class="h" > <a name="2342">2342</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> }; </td> </tr> <tr> <td class="h" > <a name="2343">2343</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2344">2344</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The point at which this data gets into the object is, of course, </td> </tr> <tr> <td class="h" > <a name="2345">2345</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<Data::Presenter::[package1]::_init()>. What the administrator does at that </td> </tr> <tr> <td class="h" > <a name="2346">2346</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> point is limited only by his/her imagination. Data::Presenter seeks to bless </td> </tr> <tr> <td class="h" > <a name="2347">2347</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> a hash into its object. That hash must meet the following requirements: </td> </tr> <tr> <td class="h" > <a name="2348">2348</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2349">2349</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =over 4 </td> </tr> <tr> <td class="h" > <a name="2350">2350</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2351">2351</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * </td> </tr> <tr> <td class="h" > <a name="2352">2352</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2353">2353</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> With the exception of elements holding metadata, each element holds an array, </td> </tr> <tr> <td class="h" > <a name="2354">2354</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> each of whose elements must be a number or a string. </td> </tr> <tr> <td class="h" > <a name="2355">2355</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2356">2356</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * </td> </tr> <tr> <td class="h" > <a name="2357">2357</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2358">2358</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Three metadata elements keyed as follows must be present: </td> </tr> <tr> <td class="h" > <a name="2359">2359</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2360">2360</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =over 4 </td> </tr> <tr> <td class="h" > <a name="2361">2361</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2362">2362</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * C<fields> </td> </tr> <tr> <td class="h" > <a name="2363">2363</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2364">2364</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * C<parameters> </td> </tr> <tr> <td class="h" > <a name="2365">2365</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2366">2366</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * C<index> </td> </tr> <tr> <td class="h" > <a name="2367">2367</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2368">2368</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =back </td> </tr> <tr> <td class="h" > <a name="2369">2369</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2370">2370</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The fourth metadata element keyed by C<options> is required only if some </td> </tr> <tr> <td class="h" > <a name="2371">2371</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Data::Presenter method has been written which requires the information stored </td> </tr> <tr> <td class="h" > <a name="2372">2372</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> therein. C<writeformat_with_reprocessing()> is the only such method currently </td> </tr> <tr> <td class="h" > <a name="2373">2373</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> present, but additional methods using the C<options> key may be added in </td> </tr> <tr> <td class="h" > <a name="2374">2374</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the future. </td> </tr> <tr> <td class="h" > <a name="2375">2375</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2376">2376</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =back </td> </tr> <tr> <td class="h" > <a name="2377">2377</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2378">2378</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The author has used two different approaches to the problem of initializing </td> </tr> <tr> <td class="h" > <a name="2379">2379</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Data::Presenter::[package1] objects. </td> </tr> <tr> <td class="h" > <a name="2380">2380</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2381">2381</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =over 4 </td> </tr> <tr> <td class="h" > <a name="2382">2382</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2383">2383</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * </td> </tr> <tr> <td class="h" > <a name="2384">2384</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2385">2385</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> In the first, more standard approach, the name of a source file can be passed </td> </tr> <tr> <td class="h" > <a name="2386">2386</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> to the constructor, which passes it on to the initializer, which then opens a </td> </tr> <tr> <td class="h" > <a name="2387">2387</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> filehandle to the file and processes with regular expressions, C<unpack>, </td> </tr> <tr> <td class="h" > <a name="2388">2388</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> etc. to build an array for each data record. Keyed by a unique ID, a </td> </tr> <tr> <td class="h" > <a name="2389">2389</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> reference to this array then becomes the value of an element of the hash </td> </tr> <tr> <td class="h" > <a name="2390">2390</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> which, once metadata is added, is blessed into the </td> </tr> <tr> <td class="h" > <a name="2391">2391</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Data::Presenter::[package1] object. The source for the metadata is the </td> </tr> <tr> <td class="h" > <a name="2392">2392</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> F<fields.[package1].data> file and the C<@fields>, C<%parameters> and </td> </tr> <tr> <td class="h" > <a name="2393">2393</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<$index> found therein. </td> </tr> <tr> <td class="h" > <a name="2394">2394</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2395">2395</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * </td> </tr> <tr> <td class="h" > <a name="2396">2396</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2397">2397</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> A second approach asks: ''Instead of having C<_init()> do data munging on a </td> </tr> <tr> <td class="h" > <a name="2398">2398</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> file, why not directly pass it a hash of arrays? Better still, why not pass </td> </tr> <tr> <td class="h" > <a name="2399">2399</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> it a hash of arrays which already has an C<'options'> key defined? And </td> </tr> <tr> <td class="h" > <a name="2400">2400</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> better still yet, why not pass it an object produced by some other Perl </td> </tr> <tr> <td class="h" > <a name="2401">2401</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> module and containing a blessed hash of arrays with an already defined </td> </tr> <tr> <td class="h" > <a name="2402">2402</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<options> key?'' In this approach, C<Data::Presenter::[package1]::_init()> </td> </tr> <tr> <td class="h" > <a name="2403">2403</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> does no data munging. It is mainly concerned with defining the three </td> </tr> <tr> <td class="h" > <a name="2404">2404</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> required metadata elements. </td> </tr> <tr> <td class="h" > <a name="2405">2405</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2406">2406</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =back </td> </tr> <tr> <td class="h" > <a name="2407">2407</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2408">2408</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head3 C<writeformat_deluxe()> </td> </tr> <tr> <td class="h" > <a name="2409">2409</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2410">2410</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeformat_deluxe()> is an advanced application of </td> </tr> <tr> <td class="h" > <a name="2411">2411</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Data::Presenter and the reader may wish to skip this section until other </td> </tr> <tr> <td class="h" > <a name="2412">2412</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> parts of the module have been mastered. </td> </tr> <tr> <td class="h" > <a name="2413">2413</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2414">2414</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeformat_deluxe()> enables the user to have I<both> column headers (as in </td> </tr> <tr> <td class="h" > <a name="2415">2415</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeformat_plus_header()>) and dynamic, 'just-in-time' reprocessing of data </td> </tr> <tr> <td class="h" > <a name="2416">2416</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> in selected fields (as in C<writeformat_with_reprocessing()>). Call it just </td> </tr> <tr> <td class="h" > <a name="2417">2417</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> as you would C<writeformat_with_reprocessing()>, but add a key-value pair </td> </tr> <tr> <td class="h" > <a name="2418">2418</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> keyed by C<title>. </td> </tr> <tr> <td class="h" > <a name="2419">2419</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2420">2420</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %reprocessing_info = ( </td> </tr> <tr> <td class="h" > <a name="2421">2421</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> timeslot => 17, </td> </tr> <tr> <td class="h" > <a name="2422">2422</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> instructor => 15, </td> </tr> <tr> <td class="h" > <a name="2423">2423</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2424">2424</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2425">2425</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $dp1->writeformat_deluxe( </td> </tr> <tr> <td class="h" > <a name="2426">2426</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> sorted => $sorted_data, </td> </tr> <tr> <td class="h" > <a name="2427">2427</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> columns => \@columns_selected, </td> </tr> <tr> <td class="h" > <a name="2428">2428</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> file => $outputfile, </td> </tr> <tr> <td class="h" > <a name="2429">2429</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> reprocess => \%reprocessing_info, </td> </tr> <tr> <td class="h" > <a name="2430">2430</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> title => $title, </td> </tr> <tr> <td class="h" > <a name="2431">2431</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2432">2432</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2433">2433</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head3 C<writedelimited()> </td> </tr> <tr> <td class="h" > <a name="2434">2434</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2435">2435</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The C<Data::Presenter::writeformat...()> family of </td> </tr> <tr> <td class="h" > <a name="2436">2436</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> methods discussed above write data to plain-text files in columns aligned </td> </tr> <tr> <td class="h" > <a name="2437">2437</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> with whitespace via Perl's C<formline> function -- the function which </td> </tr> <tr> <td class="h" > <a name="2438">2438</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> internally powers Perl formats. This is suitable if the ultimate consumer of </td> </tr> <tr> <td class="h" > <a name="2439">2439</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the data is satisfied to read a plain-text file. However, in many business </td> </tr> <tr> <td class="h" > <a name="2440">2440</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> contexts data consumers are more accustomed to word processing files than to </td> </tr> <tr> <td class="h" > <a name="2441">2441</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> plain-text files. In particular, data consumers are accustomed to data </td> </tr> <tr> <td class="h" > <a name="2442">2442</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> presented in tables created by commercial word processing programs. Such </td> </tr> <tr> <td class="h" > <a name="2443">2443</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> programs generally have the capacity to take text in which individual lines </td> </tr> <tr> <td class="h" > <a name="2444">2444</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> consist of data separated by delimiter characters such as tabs or commas and </td> </tr> <tr> <td class="h" > <a name="2445">2445</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> transform that text into rows in a table where the delimiters signal the </td> </tr> <tr> <td class="h" > <a name="2446">2446</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> borders between table cells. </td> </tr> <tr> <td class="h" > <a name="2447">2447</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2448">2448</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> To that end, the author has created the </td> </tr> <tr> <td class="h" > <a name="2449">2449</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<Data::Presenter::writedelimited...()> family of subroutines to print output </td> </tr> <tr> <td class="h" > <a name="2450">2450</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> to plain-text files intended for further processing within word processing </td> </tr> <tr> <td class="h" > <a name="2451">2451</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> programs. The simplest method in this family, C<writedelimited()>, takes a </td> </tr> <tr> <td class="h" > <a name="2452">2452</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> list of three key-value pairs: </td> </tr> <tr> <td class="h" > <a name="2453">2453</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2454">2454</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =over 4 </td> </tr> <tr> <td class="h" > <a name="2455">2455</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2456">2456</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * C<sorted> </td> </tr> <tr> <td class="h" > <a name="2457">2457</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2458">2458</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The value keyed by C<sorted> is a hash reference which is the return value of </td> </tr> <tr> <td class="h" > <a name="2459">2459</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<sort_by_column()>. Hence, C<writedelimited()> can only be called once </td> </tr> <tr> <td class="h" > <a name="2460">2460</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<sort_by_column()> has been called. </td> </tr> <tr> <td class="h" > <a name="2461">2461</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2462">2462</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * C<file> </td> </tr> <tr> <td class="h" > <a name="2463">2463</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2464">2464</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The value keyed by C<file> is the name of a file arbitrarily selected by </td> </tr> <tr> <td class="h" > <a name="2465">2465</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the operator to hold the output of C<writedelimited()>. </td> </tr> <tr> <td class="h" > <a name="2466">2466</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2467">2467</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * C<delimiter> </td> </tr> <tr> <td class="h" > <a name="2468">2468</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2469">2469</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The value keyed by C<delimiter> is the user-selected delimiter character or </td> </tr> <tr> <td class="h" > <a name="2470">2470</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> characters which will delineate fields within an individual record in the </td> </tr> <tr> <td class="h" > <a name="2471">2471</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> output file. Typically, this character will be a tab (C<\t>), comma (C<,>) </td> </tr> <tr> <td class="h" > <a name="2472">2472</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> or similar character that a word processing program's 'convert text to table' </td> </tr> <tr> <td class="h" > <a name="2473">2473</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> feature can use to establish columns. </td> </tr> <tr> <td class="h" > <a name="2474">2474</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2475">2475</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =back </td> </tr> <tr> <td class="h" > <a name="2476">2476</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2477">2477</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Using the ''census'' example from above, the overall sequence of code needed </td> </tr> <tr> <td class="h" > <a name="2478">2478</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> to use C<writedelimited()> would be: </td> </tr> <tr> <td class="h" > <a name="2479">2479</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2480">2480</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> @columns_selected = ('lastname', 'firstname', 'datebirth', 'cno'); </td> </tr> <tr> <td class="h" > <a name="2481">2481</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $sorted_data = $dp1->sort_by_column(\@columns_selected); </td> </tr> <tr> <td class="h" > <a name="2482">2482</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2483">2483</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $dp1->writedelimited( </td> </tr> <tr> <td class="h" > <a name="2484">2484</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> sorted => $sorted_data, </td> </tr> <tr> <td class="h" > <a name="2485">2485</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> file => $outputfile, </td> </tr> <tr> <td class="h" > <a name="2486">2486</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> delimiter => $delimiter, </td> </tr> <tr> <td class="h" > <a name="2487">2487</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2488">2488</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2489">2489</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Note that, unlike C<writeformat()>, C<writedelimited()> does not require a </td> </tr> <tr> <td class="h" > <a name="2490">2490</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> reference to C<@columns_selected> to be passed as an argument. </td> </tr> <tr> <td class="h" > <a name="2491">2491</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2492">2492</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Depending on the number of characters in a text editor's tab-stop setting, </td> </tr> <tr> <td class="h" > <a name="2493">2493</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the result of the above call might look like: </td> </tr> <tr> <td class="h" > <a name="2494">2494</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2495">2495</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> HERNANDEZ HECTOR 1963-08-01 456791 </td> </tr> <tr> <td class="h" > <a name="2496">2496</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> VASQUEZ ADALBERTO 1973-08-17 786792 </td> </tr> <tr> <td class="h" > <a name="2497">2497</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> VASQUEZ ALBERTO 1953-02-28 906786 </td> </tr> <tr> <td class="h" > <a name="2498">2498</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2499">2499</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> This is obviously less readable than the output of C<writeformat()> -- but </td> </tr> <tr> <td class="h" > <a name="2500">2500</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> since the output of C<writedelimited()> is intended for further processing by </td> </tr> <tr> <td class="h" > <a name="2501">2501</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> a word processing program rather than for final use, this is not a major </td> </tr> <tr> <td class="h" > <a name="2502">2502</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> concern. </td> </tr> <tr> <td class="h" > <a name="2503">2503</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2504">2504</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head3 C<writedelimited_plus_header()> </td> </tr> <tr> <td class="h" > <a name="2505">2505</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2506">2506</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Just as C<writeformat_plus_header()> extended </td> </tr> <tr> <td class="h" > <a name="2507">2507</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeformat()> to include column headers, C<writedelimited_plus_header()> </td> </tr> <tr> <td class="h" > <a name="2508">2508</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> extends C<writedelimited()> to include column headers, separated by the same </td> </tr> <tr> <td class="h" > <a name="2509">2509</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> delimiter character as the data, in a plain-text file intended for further </td> </tr> <tr> <td class="h" > <a name="2510">2510</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> processing by a word processing program. </td> </tr> <tr> <td class="h" > <a name="2511">2511</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2512">2512</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writedelimited_plus_header()> takes a list of four key-value pairs: </td> </tr> <tr> <td class="h" > <a name="2513">2513</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<sorted>, C<columns>, C<file>, and C<delimiter>. The complete call </td> </tr> <tr> <td class="h" > <a name="2514">2514</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> to C<writedelimited_plus_header> looks like this: </td> </tr> <tr> <td class="h" > <a name="2515">2515</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2516">2516</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> @columns_selected = ( </td> </tr> <tr> <td class="h" > <a name="2517">2517</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 'unit', 'ward', 'lastname', 'firstname', </td> </tr> <tr> <td class="h" > <a name="2518">2518</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> 'datebirth', 'dateadmission', 'cno'); </td> </tr> <tr> <td class="h" > <a name="2519">2519</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $sorted_data = $dp1->sort_by_column(\@columns_selected); </td> </tr> <tr> <td class="h" > <a name="2520">2520</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2521">2521</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $dp1->writedelimited_plus_header( </td> </tr> <tr> <td class="h" > <a name="2522">2522</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> sorted => $sorted_data, </td> </tr> <tr> <td class="h" > <a name="2523">2523</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> columns => \@columns_selected, </td> </tr> <tr> <td class="h" > <a name="2524">2524</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> file => $outputfile, </td> </tr> <tr> <td class="h" > <a name="2525">2525</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> delimiter => $delimiter, </td> </tr> <tr> <td class="h" > <a name="2526">2526</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2527">2527</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2528">2528</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Note that, unlike C<writeformat_plus_header()>, C<writedelimited_plus_header()> </td> </tr> <tr> <td class="h" > <a name="2529">2529</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> does not take C<$title> as an argument. It is felt that any title would be </td> </tr> <tr> <td class="h" > <a name="2530">2530</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> more likely to be supplied in the word-processing file which ultimately holds </td> </tr> <tr> <td class="h" > <a name="2531">2531</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the data prepared by C<writedelimited_plus_header()> and that its inclusion </td> </tr> <tr> <td class="h" > <a name="2532">2532</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> at this point might interfere with the workings of the word processing </td> </tr> <tr> <td class="h" > <a name="2533">2533</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> program's 'convert text to table' feature. </td> </tr> <tr> <td class="h" > <a name="2534">2534</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2535">2535</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Depending on the number of characters in a text editor's tab-stop setting, </td> </tr> <tr> <td class="h" > <a name="2536">2536</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the result of the above call might look like: </td> </tr> <tr> <td class="h" > <a name="2537">2537</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2538">2538</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Date Date of </td> </tr> <tr> <td class="h" > <a name="2539">2539</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Unit Ward Last Name First Name of Birth Admission C No. </td> </tr> <tr> <td class="h" > <a name="2540">2540</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> LAVER 0105 VASQUEZ JORGE 1956-01-13 1986-01-17 456787 </td> </tr> <tr> <td class="h" > <a name="2541">2541</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> LAVER 0107 VASQUEZ LEONARDO 1970-15-23 1990-08-23 456788 </td> </tr> <tr> <td class="h" > <a name="2542">2542</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> SAMSON 0209 VASQUEZ JOAQUIN 1970-03-25 1990-11-14 456789 </td> </tr> <tr> <td class="h" > <a name="2543">2543</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2544">2544</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Again, the readability of the delimited copy in the plain-text file here is </td> </tr> <tr> <td class="h" > <a name="2545">2545</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> not as important as how correctly the delimiter has been chosen in order to </td> </tr> <tr> <td class="h" > <a name="2546">2546</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> produce good results once the file is further processed by a word processing </td> </tr> <tr> <td class="h" > <a name="2547">2547</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> program. </td> </tr> <tr> <td class="h" > <a name="2548">2548</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2549">2549</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Note that, unlike C<writeformat_plus_header()>, C<writedelimited_plus_header()> </td> </tr> <tr> <td class="h" > <a name="2550">2550</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> does not produce a hyphen line. The author feels that the separation of </td> </tr> <tr> <td class="h" > <a name="2551">2551</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> header and body within the table is here better handled within the word </td> </tr> <tr> <td class="h" > <a name="2552">2552</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> processing file which ultimately holds the data prepared by </td> </tr> <tr> <td class="h" > <a name="2553">2553</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writedelimited_plus_header()>. </td> </tr> <tr> <td class="h" > <a name="2554">2554</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2555">2555</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Note further that, unlike C<writeformat_plus_header()>, </td> </tr> <tr> <td class="h" > <a name="2556">2556</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writedelimited_plus_header()> does not truncate the words in column headers. </td> </tr> <tr> <td class="h" > <a name="2557">2557</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> This is because the C<writedelimited...()> family of methods does not impose </td> </tr> <tr> <td class="h" > <a name="2558">2558</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> a maximum width on output fields as does the C<writeformat...()> family of </td> </tr> <tr> <td class="h" > <a name="2559">2559</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> methods. Hence, there is no need to truncate headers to fit within specified </td> </tr> <tr> <td class="h" > <a name="2560">2560</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> column widths. Column widths in the C<writedelimited...()> family are </td> </tr> <tr> <td class="h" > <a name="2561">2561</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ultimately determined by the word processing program which produces the final </td> </tr> <tr> <td class="h" > <a name="2562">2562</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> output. </td> </tr> <tr> <td class="h" > <a name="2563">2563</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2564">2564</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head3 C<writedelimited_with_reprocessing()> </td> </tr> <tr> <td class="h" > <a name="2565">2565</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2566">2566</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writedelimited_with_reprocessing()> </td> </tr> <tr> <td class="h" > <a name="2567">2567</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> is an advanced application of Data::Presenter and the reader may wish to skip </td> </tr> <tr> <td class="h" > <a name="2568">2568</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> this section until other parts of the module have been mastered. </td> </tr> <tr> <td class="h" > <a name="2569">2569</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2570">2570</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writedelimited_with_reprocessing()>, like C<writeformat_with_reprocessing()>, </td> </tr> <tr> <td class="h" > <a name="2571">2571</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> permits a sophisticated administrator to activate ''last minute'' </td> </tr> <tr> <td class="h" > <a name="2572">2572</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> substitutions in strings to be printed such that substitutions do not affect </td> </tr> <tr> <td class="h" > <a name="2573">2573</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the pre-established sorting order. For a full discussion of the rationale </td> </tr> <tr> <td class="h" > <a name="2574">2574</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> for this feature, see the discussion of L<"writeformat_with_reprocessing()"> </td> </tr> <tr> <td class="h" > <a name="2575">2575</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> above. </td> </tr> <tr> <td class="h" > <a name="2576">2576</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2577">2577</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writedelimited_with_reprocessing()> takes a list of five key-value pairs, </td> </tr> <tr> <td class="h" > <a name="2578">2578</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> four of which are the same arguments passed to </td> </tr> <tr> <td class="h" > <a name="2579">2579</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeformat_with_reprocessing()>. The fifth key-value pair is a reference </td> </tr> <tr> <td class="h" > <a name="2580">2580</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> to an array holding a list of those columns selected for output upon which </td> </tr> <tr> <td class="h" > <a name="2581">2581</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the user chooses to perform reprocessing. </td> </tr> <tr> <td class="h" > <a name="2582">2582</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2583">2583</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> @reprocessing_info = qw( instructor timeslot room ); </td> </tr> <tr> <td class="h" > <a name="2584">2584</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2585">2585</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $dp1->writedelimited_with_reprocessing( </td> </tr> <tr> <td class="h" > <a name="2586">2586</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> sorted => $sorted_data, </td> </tr> <tr> <td class="h" > <a name="2587">2587</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> columns => \@columns_selected, </td> </tr> <tr> <td class="h" > <a name="2588">2588</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> file => $outputfile, </td> </tr> <tr> <td class="h" > <a name="2589">2589</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> delimiter => $delimiter, </td> </tr> <tr> <td class="h" > <a name="2590">2590</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> reprocess => \@reprocessing_info, </td> </tr> <tr> <td class="h" > <a name="2591">2591</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2592">2592</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2593">2593</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Taking the classroom scheduling problem presented above, </td> </tr> <tr> <td class="h" > <a name="2594">2594</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writedelimited_with_reprocessing()> would produce output looking something </td> </tr> <tr> <td class="h" > <a name="2595">2595</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> like this: </td> </tr> <tr> <td class="h" > <a name="2596">2596</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2597">2597</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 9:00 Arithmetic E Jones 4044 4044_11 </td> </tr> <tr> <td class="h" > <a name="2598">2598</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 9:00 Language Studies T Wilson 4054 4054_11 </td> </tr> <tr> <td class="h" > <a name="2599">2599</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 10:00 Bible Study M Eliade 4068 4068_12 </td> </tr> <tr> <td class="h" > <a name="2600">2600</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 10:00 Introduction to Computers D Knuth 4086 4086_12 </td> </tr> <tr> <td class="h" > <a name="2601">2601</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 11:00 Psychology A Adler 4077 4077_13 </td> </tr> <tr> <td class="h" > <a name="2602">2602</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 11:00 Social Science T Jones 4044 4044_13 </td> </tr> <tr> <td class="h" > <a name="2603">2603</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Friday, 9:00 World History H Wells 4052 4052_51 </td> </tr> <tr> <td class="h" > <a name="2604">2604</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Friday, 9:00 Music Appreciation W Wilson 4044 4044_51 </td> </tr> <tr> <td class="h" > <a name="2605">2605</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2606">2606</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Usage of C<writedelimited_with_reprocessing()> requires that the administrator </td> </tr> <tr> <td class="h" > <a name="2607">2607</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> appropriately define C<Data::Presenter::[Package1]::_reprocess_delimit()> and </td> </tr> <tr> <td class="h" > <a name="2608">2608</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<Data::Presenter::[Package1]::_init()> subroutines in the invoking package, </td> </tr> <tr> <td class="h" > <a name="2609">2609</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> along with appropriate subroutines specific to each argument capable of being </td> </tr> <tr> <td class="h" > <a name="2610">2610</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> reprocessed. Again, see the discussion in L<"writeformat_with_reprocessing()">. </td> </tr> <tr> <td class="h" > <a name="2611">2611</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2612">2612</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head3 C<writedelimited_deluxe()> </td> </tr> <tr> <td class="h" > <a name="2613">2613</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2614">2614</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writedelimited_deluxe()> is an advanced </td> </tr> <tr> <td class="h" > <a name="2615">2615</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> application of Data::Presenter and the reader may wish to skip this section </td> </tr> <tr> <td class="h" > <a name="2616">2616</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> until other parts of the module have been mastered. </td> </tr> <tr> <td class="h" > <a name="2617">2617</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2618">2618</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writedelimited_deluxe()> completes the parallel structure between the </td> </tr> <tr> <td class="h" > <a name="2619">2619</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeformat...()> and C<writedelimited...()> families of Data::Presenter </td> </tr> <tr> <td class="h" > <a name="2620">2620</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> methods by enabling the user to have I<both> column headers (as in </td> </tr> <tr> <td class="h" > <a name="2621">2621</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writedelimited_plus_header()>) and dynamic, 'just-in-time' reprocessing of </td> </tr> <tr> <td class="h" > <a name="2622">2622</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> data in selected fields (as in C<writedelimited_with_reprocessing()>). Except </td> </tr> <tr> <td class="h" > <a name="2623">2623</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> for the name of the method called, the call to C<writedelimited_deluxe()> is </td> </tr> <tr> <td class="h" > <a name="2624">2624</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the same as for C<writedelimited_with_reprocessing()>: </td> </tr> <tr> <td class="h" > <a name="2625">2625</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2626">2626</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> @reprocessing_info = qw( instructor timeslot ); </td> </tr> <tr> <td class="h" > <a name="2627">2627</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2628">2628</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $dp1->writedelimited_deluxe( </td> </tr> <tr> <td class="h" > <a name="2629">2629</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> sorted => $sorted_data, </td> </tr> <tr> <td class="h" > <a name="2630">2630</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> columns => \@columns_selected, </td> </tr> <tr> <td class="h" > <a name="2631">2631</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> file => $outputfile, </td> </tr> <tr> <td class="h" > <a name="2632">2632</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> delimiter => $delimiter, </td> </tr> <tr> <td class="h" > <a name="2633">2633</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> reprocess => \@reprocessing_info, </td> </tr> <tr> <td class="h" > <a name="2634">2634</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2635">2635</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2636">2636</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Using the classroom scheduling example from above,the output from </td> </tr> <tr> <td class="h" > <a name="2637">2637</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writedelimited_deluxe()> might look like this: </td> </tr> <tr> <td class="h" > <a name="2638">2638</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2639">2639</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Timeslot Group Instructor Room GroupID </td> </tr> <tr> <td class="h" > <a name="2640">2640</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 9:00 Arithmetic E Jones 4044 4044_11 </td> </tr> <tr> <td class="h" > <a name="2641">2641</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 9:00 Language Studies T Wilson 4054 4054_11 </td> </tr> <tr> <td class="h" > <a name="2642">2642</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 10:00 Bible Study M Eliade 4068 4068_12 </td> </tr> <tr> <td class="h" > <a name="2643">2643</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 10:00 Introduction to Computers D Knuth 4086 4086_12 </td> </tr> <tr> <td class="h" > <a name="2644">2644</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 11:00 Psychology A Adler 4077 4077_13 </td> </tr> <tr> <td class="h" > <a name="2645">2645</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Monday, 11:00 Social Science T Jones 4044 4044_13 </td> </tr> <tr> <td class="h" > <a name="2646">2646</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Friday, 9:00 World History H Wells 4052 4052_51 </td> </tr> <tr> <td class="h" > <a name="2647">2647</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Friday, 9:00 Music Appreciation W Wilson 4044 4044_51 </td> </tr> <tr> <td class="h" > <a name="2648">2648</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2649">2649</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> As with C<writedelimited_with_reprocessing()>, C<writedelimited_deluxe()> </td> </tr> <tr> <td class="h" > <a name="2650">2650</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> requires careful preparation on the part of the administrator. See the </td> </tr> <tr> <td class="h" > <a name="2651">2651</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> discussion under L<"writeformat_with_reprocessing()"> above. </td> </tr> <tr> <td class="h" > <a name="2652">2652</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2653">2653</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head3 C<writeHTML()> </td> </tr> <tr> <td class="h" > <a name="2654">2654</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2655">2655</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> In its current formulation, C<writeHTML()> works very much </td> </tr> <tr> <td class="h" > <a name="2656">2656</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> like C<writeformat_plus_header()>. It writes data to an operator-specified </td> </tr> <tr> <td class="h" > <a name="2657">2657</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> HTML file and writes an appropriate header to that file as well. </td> </tr> <tr> <td class="h" > <a name="2658">2658</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<writeHTML()> takes the same 4 arguments as C<writeformat_plus_header()>: </td> </tr> <tr> <td class="h" > <a name="2659">2659</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<$sorted_data>, C<\@columns_selected>, C<$outputfile> and C<$title>. The </td> </tr> <tr> <td class="h" > <a name="2660">2660</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> body of the resulting HTML file is more similar to a Perl format than to an </td> </tr> <tr> <td class="h" > <a name="2661">2661</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> HTML table. (This may be upgraded to a true HTML table in a future release.) </td> </tr> <tr> <td class="h" > <a name="2662">2662</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2663">2663</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $dp1->writeHTML( </td> </tr> <tr> <td class="h" > <a name="2664">2664</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> sorted => $sorted_data, </td> </tr> <tr> <td class="h" > <a name="2665">2665</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> columns => \@columns_selected, </td> </tr> <tr> <td class="h" > <a name="2666">2666</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> file => $HTMLoutputfile, # must have .html extension </td> </tr> <tr> <td class="h" > <a name="2667">2667</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> title => $title, </td> </tr> <tr> <td class="h" > <a name="2668">2668</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2669">2669</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2670">2670</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head2 Data::Presenter::Combo Objects </td> </tr> <tr> <td class="h" > <a name="2671">2671</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2672">2672</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> It is quite possible that we may have two or more different database reports </td> </tr> <tr> <td class="h" > <a name="2673">2673</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> which present data on the same underlying universe or population. If these </td> </tr> <tr> <td class="h" > <a name="2674">2674</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> reports share a common index field which can be used to uniquely identify </td> </tr> <tr> <td class="h" > <a name="2675">2675</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> each entry in the underlying population, then we would like to be able to </td> </tr> <tr> <td class="h" > <a name="2676">2676</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> combine these sources, manipulate the data and re-output them via the simple </td> </tr> <tr> <td class="h" > <a name="2677">2677</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> and complex Data::Presenter output methods described in the L<"Synopsis"> </td> </tr> <tr> <td class="h" > <a name="2678">2678</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> above. </td> </tr> <tr> <td class="h" > <a name="2679">2679</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2680">2680</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> In other words, if we have already created </td> </tr> <tr> <td class="h" > <a name="2681">2681</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2682">2682</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $dp1 = Data::Presenter::[Package1]->new( </td> </tr> <tr> <td class="h" > <a name="2683">2683</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $sourcefile, \@fields,\%parameters, $index); </td> </tr> <tr> <td class="h" > <a name="2684">2684</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $dp2 = Data::Presenter::[Package2]->new( </td> </tr> <tr> <td class="h" > <a name="2685">2685</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $sourcefile, \@fields,\%parameters, $index); </td> </tr> <tr> <td class="h" > <a name="2686">2686</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ... </td> </tr> <tr> <td class="h" > <a name="2687">2687</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $dpx = Data::Presenter::[Package2]->new( </td> </tr> <tr> <td class="h" > <a name="2688">2688</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $sourcefile, \@fields,\%parameters, $index); </td> </tr> <tr> <td class="h" > <a name="2689">2689</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2690">2690</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> we would like to be able to define an array of the objects we have created </td> </tr> <tr> <td class="h" > <a name="2691">2691</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> and construct a new object combining the first two in an orderly manner: </td> </tr> <tr> <td class="h" > <a name="2692">2692</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2693">2693</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my @objects = ($dp1, $dp2, ... $dpx); </td> </tr> <tr> <td class="h" > <a name="2694">2694</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $dpC = Data::Presenter::[some subclass]->new(\@objects); </td> </tr> <tr> <td class="h" > <a name="2695">2695</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2696">2696</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> We would then like to be able to call all the Data::Presenter sorting, </td> </tr> <tr> <td class="h" > <a name="2697">2697</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> selecting and output methods discussed above on C<$dpC> B<without having to </td> </tr> <tr> <td class="h" > <a name="2698">2698</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> re-specify C<$sourcefile>, C<\@fields>, C<\%parameters> or C<$index>>. </td> </tr> <tr> <td class="h" > <a name="2699">2699</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2700">2700</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Can we do this? Yes, we can. More precisely, we can create I<two> new types </td> </tr> <tr> <td class="h" > <a name="2701">2701</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> of objects: one in which the data entries comprise those entries found in </td> </tr> <tr> <td class="h" > <a name="2702">2702</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> I<each> of the original sources, and one in which the data entries comprise </td> </tr> <tr> <td class="h" > <a name="2703">2703</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> those found in I<any> of the sources. In mathematical terms, we can create </td> </tr> <tr> <td class="h" > <a name="2704">2704</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> either a new object which represents the I<intersection> of the sources or </td> </tr> <tr> <td class="h" > <a name="2705">2705</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> one which represents the I<union> of the sources. We call these as follows: </td> </tr> <tr> <td class="h" > <a name="2706">2706</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2707">2707</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $dpI = Data::Presenter::Combo::Intersect->new(\@objects); </td> </tr> <tr> <td class="h" > <a name="2708">2708</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2709">2709</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> and </td> </tr> <tr> <td class="h" > <a name="2710">2710</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2711">2711</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $dpU = Data::Presenter::Combo::Union->new(\@objects); </td> </tr> <tr> <td class="h" > <a name="2712">2712</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2713">2713</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Note the following: </td> </tr> <tr> <td class="h" > <a name="2714">2714</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2715">2715</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =over 4 </td> </tr> <tr> <td class="h" > <a name="2716">2716</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2717">2717</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * </td> </tr> <tr> <td class="h" > <a name="2718">2718</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2719">2719</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> For Combo objects, unlike all other Data::Presenter::[Package1] objects, we </td> </tr> <tr> <td class="h" > <a name="2720">2720</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> pass only one variable -- a reference to an array of Data::Presenter objects </td> </tr> <tr> <td class="h" > <a name="2721">2721</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> -- to the constructor instead of three. </td> </tr> <tr> <td class="h" > <a name="2722">2722</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2723">2723</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * </td> </tr> <tr> <td class="h" > <a name="2724">2724</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2725">2725</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Combo objects are always called from a subclass of Data::Presenter::Combo </td> </tr> <tr> <td class="h" > <a name="2726">2726</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> such as Data::Presenter::Combo::Intersect or Data::Presenter::Combo::Union. </td> </tr> <tr> <td class="h" > <a name="2727">2727</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> They are not called from Data::Presenter::Combo itself. </td> </tr> <tr> <td class="h" > <a name="2728">2728</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2729">2729</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * </td> </tr> <tr> <td class="h" > <a name="2730">2730</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2731">2731</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The regular Data::Presenter objects which are selected to make up a </td> </tr> <tr> <td class="h" > <a name="2732">2732</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Data::Presenter::Combo object must share a field which serves as the L<index </td> </tr> <tr> <td class="h" > <a name="2733">2733</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> field|"Index Field"> for each object. This field must carry the same name in </td> </tr> <tr> <td class="h" > <a name="2734">2734</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<@fields> in the I<fields.XXX.data> configuration files corresponding to each of </td> </tr> <tr> <td class="h" > <a name="2735">2735</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the objects, though that field does not have to appear in the same element </td> </tr> <tr> <td class="h" > <a name="2736">2736</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> position in C<@fields> in each such file. Similarly, the parameters on the </td> </tr> <tr> <td class="h" > <a name="2737">2737</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> value side of C<%parameters> for the index field must be specified </td> </tr> <tr> <td class="h" > <a name="2738">2738</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> identically in each configuration file. If these conditions are not met, a </td> </tr> <tr> <td class="h" > <a name="2739">2739</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Data::Presenter::Combo object cannot be constructed and the program will die </td> </tr> <tr> <td class="h" > <a name="2740">2740</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> with an error message. </td> </tr> <tr> <td class="h" > <a name="2741">2741</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2742">2742</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Let us illlustrate this point. Suppose that we have two configuration files, </td> </tr> <tr> <td class="h" > <a name="2743">2743</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> I<fields1.data> and I<fields2.data>, corresponding to two different </td> </tr> <tr> <td class="h" > <a name="2744">2744</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Data::Presenter objects, C<$obj1> and C<$obj2>. For I<fields1.data>, we </td> </tr> <tr> <td class="h" > <a name="2745">2745</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> have: </td> </tr> <tr> <td class="h" > <a name="2746">2746</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2747">2747</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> @fields = qw(lastname, firstname, cno); </td> </tr> <tr> <td class="h" > <a name="2748">2748</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2749">2749</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %parameters = ( </td> </tr> <tr> <td class="h" > <a name="2750">2750</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $fields[0] => [14, 'U', 'a', 'Last Name'], </td> </tr> <tr> <td class="h" > <a name="2751">2751</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $fields[1] => [10, 'U', 'a', 'First Name'], </td> </tr> <tr> <td class="h" > <a name="2752">2752</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $fields[2] => [ 7, 'U', 'n', 'C No.'], </td> </tr> <tr> <td class="h" > <a name="2753">2753</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2754">2754</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2755">2755</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $index = 2; </td> </tr> <tr> <td class="h" > <a name="2756">2756</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2757">2757</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> For I<fields2.data>, we have: </td> </tr> <tr> <td class="h" > <a name="2758">2758</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2759">2759</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> @fields = qw(cno, dateadmission, datebirth); </td> </tr> <tr> <td class="h" > <a name="2760">2760</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2761">2761</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %parameters = ( </td> </tr> <tr> <td class="h" > <a name="2762">2762</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $fields[0] => [ 7, 'U', 'n', 'C No.'], </td> </tr> <tr> <td class="h" > <a name="2763">2763</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $fields[1] => [10, 'U', 'a', 'Date of Admission'], </td> </tr> <tr> <td class="h" > <a name="2764">2764</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $fields[2] => [10, 'U', 'a', 'Date of Birth'], </td> </tr> <tr> <td class="h" > <a name="2765">2765</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2766">2766</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2767">2767</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $index = 0; </td> </tr> <tr> <td class="h" > <a name="2768">2768</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2769">2769</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Can C<$obj1> and C<$obj2> be combined into a Data::Presenter::Combo object? </td> </tr> <tr> <td class="h" > <a name="2770">2770</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Yes, they can. C<cno> is named as the index field in each configuration </td> </tr> <tr> <td class="h" > <a name="2771">2771</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> file, and the values assigned to C<$fields[$index]> in each are identical: </td> </tr> <tr> <td class="h" > <a name="2772">2772</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<[ 7, 'U', 'n', 'C No.']>. </td> </tr> <tr> <td class="h" > <a name="2773">2773</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2774">2774</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Suppose, however, that we had a third configuration file, I<fields3.data>, </td> </tr> <tr> <td class="h" > <a name="2775">2775</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> corresponding to yet another Data::Presenter object, C<$obj3>. If the </td> </tr> <tr> <td class="h" > <a name="2776">2776</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> contents of I<fields3.data> were: </td> </tr> <tr> <td class="h" > <a name="2777">2777</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2778">2778</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> @fields = qw(cno, dateadmission, datebirth); </td> </tr> <tr> <td class="h" > <a name="2779">2779</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2780">2780</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %parameters = ( </td> </tr> <tr> <td class="h" > <a name="2781">2781</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $fields[0] => [ 7, 'U', 'n', 'Serial No.'], </td> </tr> <tr> <td class="h" > <a name="2782">2782</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $fields[1] => [10, 'U', 'a', 'Date of Admission'], </td> </tr> <tr> <td class="h" > <a name="2783">2783</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $fields[2] => [10, 'U', 'a', 'Date of Birth'], </td> </tr> <tr> <td class="h" > <a name="2784">2784</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> ); </td> </tr> <tr> <td class="h" > <a name="2785">2785</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2786">2786</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $index = 0; </td> </tr> <tr> <td class="h" > <a name="2787">2787</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2788">2788</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> then C<$obj3> could not be combined with either C<$obj1> or C<$obj2> because </td> </tr> <tr> <td class="h" > <a name="2789">2789</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the elements of C<$parameters{$fields[$index]}> in C<$obj3> are not identical </td> </tr> <tr> <td class="h" > <a name="2790">2790</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> to those in the first two objects. </td> </tr> <tr> <td class="h" > <a name="2791">2791</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2792">2792</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =back </td> </tr> <tr> <td class="h" > <a name="2793">2793</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2794">2794</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Here are some things to consider in using Data::Presenter::Combo objects: </td> </tr> <tr> <td class="h" > <a name="2795">2795</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2796">2796</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =over 4 </td> </tr> <tr> <td class="h" > <a name="2797">2797</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2798">2798</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * </td> </tr> <tr> <td class="h" > <a name="2799">2799</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2800">2800</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Q: What happens if C<$dp1> has entries not found in C<$dp2> (or vice versa)? </td> </tr> <tr> <td class="h" > <a name="2801">2801</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2802">2802</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> A: It depends on whether you are interested in only those entries found in </td> </tr> <tr> <td class="h" > <a name="2803">2803</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> each of the data sources (the mathematical intersection of the sources) or </td> </tr> <tr> <td class="h" > <a name="2804">2804</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> those found in any of the sources (the mathematical union). Only those </td> </tr> <tr> <td class="h" > <a name="2805">2805</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> entries found in I<both> C<$dp1> and C<$dp2> are included in a </td> </tr> <tr> <td class="h" > <a name="2806">2806</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Data::Presenter::Combo::Intersect object. But if you are constructing a </td> </tr> <tr> <td class="h" > <a name="2807">2807</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Data::Presenter::Combo::Union object, any entry found in either source file </td> </tr> <tr> <td class="h" > <a name="2808">2808</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> will be represented in the Union object. These properties would hold no </td> </tr> <tr> <td class="h" > <a name="2809">2809</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> matter how many sources you used as arguments. </td> </tr> <tr> <td class="h" > <a name="2810">2810</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2811">2811</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item * </td> </tr> <tr> <td class="h" > <a name="2812">2812</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2813">2813</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Q: What happens if both C<$dp1> and C<$dp2> have fields named, for instance, </td> </tr> <tr> <td class="h" > <a name="2814">2814</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<'lastname'>? </td> </tr> <tr> <td class="h" > <a name="2815">2815</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2816">2816</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> A: Left-to-right precedence determines which object's C<'lastname'> field is </td> </tr> <tr> <td class="h" > <a name="2817">2817</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> entered into C<$dpC>. Assuming that C<$dp1> is listed first in C<@objects>, </td> </tr> <tr> <td class="h" > <a name="2818">2818</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> I<all> the fields in C<$dp1> will appear in C<$dpC>. Only those fields in </td> </tr> <tr> <td class="h" > <a name="2819">2819</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<$dp2> I<not> found in C<$dp1> will be added to C<$dpC>. If, however, </td> </tr> <tr> <td class="h" > <a name="2820">2820</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<@objects> were defined as C<($dp2, $dp1)>, then C<$dp2>'s fields would have </td> </tr> <tr> <td class="h" > <a name="2821">2821</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> precedence over those of C<$dp1>. If a C<$dp3> object were constructed based </td> </tr> <tr> <td class="h" > <a name="2822">2822</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> on yet another data source, only those fields entries I<not> found in C<$dp1> </td> </tr> <tr> <td class="h" > <a name="2823">2823</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> or C<$dp2> would be included in the Combo object -- and so forth. This </td> </tr> <tr> <td class="h" > <a name="2824">2824</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> left-to-right precedence rule governs both the data entries in C<$dpC> as </td> </tr> <tr> <td class="h" > <a name="2825">2825</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> well as the selection, sorting and output characteristics. </td> </tr> <tr> <td class="h" > <a name="2826">2826</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2827">2827</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =back </td> </tr> <tr> <td class="h" > <a name="2828">2828</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2829">2829</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head1 BUGS </td> </tr> <tr> <td class="h" > <a name="2830">2830</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2831">2831</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> It was discovered that in versions 0.68 and earlier, C<sort_by_column()> </td> </tr> <tr> <td class="h" > <a name="2832">2832</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> failed to sort data properly in descending order. This has been fixed. </td> </tr> <tr> <td class="h" > <a name="2833">2833</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> See F<Changes>. </td> </tr> <tr> <td class="h" > <a name="2834">2834</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2835">2835</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head1 REFERENCES </td> </tr> <tr> <td class="h" > <a name="2836">2836</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2837">2837</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The fundamental reference for this program is, of course, the Camel book: </td> </tr> <tr> <td class="h" > <a name="2838">2838</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Larry Wall, Tom Christiansen, Jon Orwant. <I<Programming Perl>, 3rd ed. </td> </tr> <tr> <td class="h" > <a name="2839">2839</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> O'Reilly & Associates, 2000, L<http://www.oreilly.com/catalog/pperl3/>. </td> </tr> <tr> <td class="h" > <a name="2840">2840</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2841">2841</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> A careful reading of the code will tell any competent Perl hacker that many </td> </tr> <tr> <td class="h" > <a name="2842">2842</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> tricks were taken from the Ram book: Tom Christiansen & Nathan Torkington. </td> </tr> <tr> <td class="h" > <a name="2843">2843</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> I<Perl Cookbook>. O'Reilly & Associates, 1998, </td> </tr> <tr> <td class="h" > <a name="2844">2844</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> L<http://www.oreilly.com/catalog/cookbook/>. </td> </tr> <tr> <td class="h" > <a name="2845">2845</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2846">2846</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The object-oriented programming skills needed to develop this program were </td> </tr> <tr> <td class="h" > <a name="2847">2847</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> learned via extensive re-reading of Chapters 3, 6 and 7 of Damian Conway's </td> </tr> <tr> <td class="h" > <a name="2848">2848</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> I<Object Oriented Perl>. Manning Publications, 2000, </td> </tr> <tr> <td class="h" > <a name="2849">2849</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> L<http://www.manning.com/Conway/index.html>. </td> </tr> <tr> <td class="h" > <a name="2850">2850</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2851">2851</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> This program goes to great length to follow the principle of 'Repeated Code </td> </tr> <tr> <td class="h" > <a name="2852">2852</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> is a Mistake' L<http://www.perl.com/pub/a/2000/11/repair3.html> -- a specific </td> </tr> <tr> <td class="h" > <a name="2853">2853</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> application of the general Perl principle of Laziness. The author grasped </td> </tr> <tr> <td class="h" > <a name="2854">2854</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> this principle best following a 2001 talk by Mark-Jason Dominus </td> </tr> <tr> <td class="h" > <a name="2855">2855</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> L<http://perl.plover.com/> to the New York Perlmongers L<http://ny.pm.org/>. </td> </tr> <tr> <td class="h" > <a name="2856">2856</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2857">2857</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Most of the code in the C<_init()> subroutines was written before the author </td> </tr> <tr> <td class="h" > <a name="2858">2858</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> read I<Data Munging with Perl> L<http://www.manning.com/cross/index.html> by </td> </tr> <tr> <td class="h" > <a name="2859">2859</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Dave Cross. Nonetheless, that is an excellent discussion of the problems </td> </tr> <tr> <td class="h" > <a name="2860">2860</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> involved in understanding the structure of data sources. </td> </tr> <tr> <td class="h" > <a name="2861">2861</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2862">2862</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> The discussion of bugs in this program benefitted from discussions on the </td> </tr> <tr> <td class="h" > <a name="2863">2863</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Perl Seminar New York mailing list </td> </tr> <tr> <td class="h" > <a name="2864">2864</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> L<http://groups.yahoo.com/group/perlsemny>, particularly with Martin </td> </tr> <tr> <td class="h" > <a name="2865">2865</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Heinsdorf. </td> </tr> <tr> <td class="h" > <a name="2866">2866</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2867">2867</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Correcting the bug involving sorting in descending order entailed a complete </td> </tr> <tr> <td class="h" > <a name="2868">2868</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> rewrite of much code. This rewrite was greatly assisted by C<brian d foy> and </td> </tr> <tr> <td class="h" > <a name="2869">2869</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Tanktalus in the Perlmonks thread ''Building a sorting subroutine on the </td> </tr> <tr> <td class="h" > <a name="2870">2870</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> fly'' (L<http://perlmonks.org/?node_id=512460>). </td> </tr> <tr> <td class="h" > <a name="2871">2871</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2872">2872</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head1 AUTHOR </td> </tr> <tr> <td class="h" > <a name="2873">2873</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2874">2874</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> James E. Keenan (jkeenan@cpan.org). </td> </tr> <tr> <td class="h" > <a name="2875">2875</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2876">2876</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Creation date: October 25, 2001. </td> </tr> <tr> <td class="h" > <a name="2877">2877</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Last modification date: February 10, 2008. </td> </tr> <tr> <td class="h" > <a name="2878">2878</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Copyright (c) 2001-5 James E. Keenan. United States. </td> </tr> <tr> <td class="h" > <a name="2879">2879</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> All rights reserved. </td> </tr> <tr> <td class="h" > <a name="2880">2880</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2881">2881</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> All data presented in this documentation or in the sample files in the </td> </tr> <tr> <td class="h" > <a name="2882">2882</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> archive accompanying this documentation are dummy copy. The data was </td> </tr> <tr> <td class="h" > <a name="2883">2883</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> entirely fabricated by the author for heuristic purposes. Any resemblance to </td> </tr> <tr> <td class="h" > <a name="2884">2884</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> any person, living or dead, is coincidental. </td> </tr> <tr> <td class="h" > <a name="2885">2885</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2886">2886</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> This is free software which you may distribute under the same terms as Perl </td> </tr> <tr> <td class="h" > <a name="2887">2887</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> itself. </td> </tr> <tr> <td class="h" > <a name="2888">2888</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2889">2889</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =cut </td> </tr> <tr> <td class="h" > <a name="2890">2890</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="2891">2891</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> </table> </body> </html>