File Coverage

blib/lib/Data/XHash.pm
Criterion Covered Total %
statement 319 337 94.6
branch 150 184 81.5
condition 94 164 57.3
subroutine 42 47 89.3
pod 26 26 100.0
total 631 758 83.2


line stmt bran cond sub pod time code
1             package Data::XHash;
2              
3 5     5   180114 use 5.006;
  5         23  
  5         249  
4 5     5   32 use strict;
  5         14  
  5         188  
5 5     5   41 use warnings;
  5         18  
  5         1029  
6 5     5   55 use base qw/Exporter/;
  5         9  
  5         890  
7 5         30 use subs qw/clear delete exists fetch first_key next_key
8 5     5   9411 scalar store xhash xhashref/;
  5         133  
9 5     5   1936 use Carp;
  5         14  
  5         819  
10 5     5   36 use Scalar::Util qw/blessed/;
  5         10  
  5         45544  
11              
12             our @EXPORT_OK = (qw/&xhash &xhashref &xh &xhn &xhr &xhrn/);
13              
14             # XHash values are stored internally using a ring doubly-linked with
15             # unweakened references:
16             # {hash}{$key} => [$previous_link, $next_link, $value, $key]
17              
18             =head1 NAME
19              
20             Data::XHash - Extended, ordered hash (commonly known as an associative array
21             or map) with key-path traversal and automatic index keys
22              
23             =head1 VERSION
24              
25             Version 0.09
26              
27             =cut
28              
29             our $VERSION = '0.09';
30              
31             =head1 SYNOPSIS
32              
33             use Data::XHash;
34             use Data::XHash qw/xhash xhashref/;
35             use Data::XHash qw/xh xhn xhr xhrn/;
36              
37             $tiedhref = Data::XHash->new(); # A blessed and tied hashref
38             # Note: Don't call "tie" yourself!
39              
40             # Exports are shortcuts to call Data::XHash->new()->push()
41             # or Data::XHash->new()->pushref() for you.
42             $tiedhref = xh('auto-indexed', { key => 'value' });
43             $tiedhref = xhash('auto-indexed', { key => 'value' });
44             $tiedhref = xhashref([ 'auto-indexed', { key => 'value' } ]);
45             $tiedhref = xhn('hello', { root => { branch =>
46             [ { leaf => 'value' }, 'world' ] } }); # (nested)
47             $tiedhref = xhr([ 'auto-indexed', { key => 'value' } ]);
48             $tiedhref = xhrn([ 'hello', { root => { branch =>
49             [ { leaf => 'value' }, 'world' ] } } ]); # (nested)
50              
51             # Note: $xhash means you can use either $tiedhref or the
52             # underlying object at tied(%$tiedhref)
53              
54             ## Hash-like operations
55              
56             # Getting keys or paths
57             $value = $tiedhref->{$key};
58             $value = $tiedhref->{\@path};
59             $value = $xhash->fetch($key);
60             $value = $xhash->fetch(\@path);
61              
62             # Auto-vivify a Data::XHash at the end of the path
63             $tiedhref2 = $tiedhref1->{ [ @path, {} ] };
64             $tiedhref->{ [ @path, {} ] }->$some_xh_method(...);
65             $tiedhref = $xhash->fetch( [ @path, {} ] );
66             $xhash->fetch( [ @path, {} ] )->$some_xh_method(...);
67              
68             # Setting keys or paths
69             $tiedhref->{$key} = $value;
70             $tiedhref->{\@path} = $value;
71             $xhash->store($key, $value, %options);
72             $xhash->store(\@path, $value, %options);
73              
74             # Setting the next auto-index key
75             $tiedhref->{[]} = $value; # Recommended syntax
76             $tiedhref->{+undef} = $value;
77             $tiedhref->{[ undef ]} = $value; # Any path key may be undef
78             $xhash->store([], $value, %options);
79             $xhash->store(undef, $value, %options);
80             $xhash->store([ undef ], $value, %options);
81              
82             # Clear the xhash
83             %$tiedhref = ();
84             $xhash->clear();
85              
86             # Delete a key and get its value
87             $value = delete $tiedhref->{$key}; # or \@path
88             $value = $xhash->delete($key); # or \@path
89             $value = $xhash->delete(\%options?, @local_keys);
90              
91             # Does a key exist?
92             $boolean = exists $tiedhref->{$key}; # or \@path
93             $boolean = $xhash->exists($key); # or \@path
94              
95             # Keys and lists of keys
96             @keys = keys %$tiedhref; # All keys using iterator
97             @keys = $xhash->keys(%options); # Faster, without iterator
98             $key = $xhash->FIRSTKEY(); # Uses iterator
99             $key = $xhash->first_key();
100             $key1 = $xhash->previous_key($key2);
101             $key = $xhash->NEXTKEY(); # Uses iterator
102             $key2 = $xhash->next_key($key1);
103             $key = $xhash->last_key();
104             $key = $xhash->next_index(); # The next auto-index key
105              
106             # Values
107             @all_values = values %$tiedhref; # Uses iterator
108             @all_values = $xhash->values(); # Faster, without iterator
109             @some_values = @{%$tiedhref}{@keys}; # or pathrefs
110             @some_values = $xhash->values(\@keys); # or pathrefs
111              
112             ($key, $value) = each(%$tiedhref); # Keys/values using iterator
113              
114             # Call coderef with ($xhash, $key, $value, @more_args) for
115             # each key/value pair and then undef/undef.
116             @results = $xhash->foreach(\&coderef, @more_args);
117              
118             # Does the hash contain any key/value pairs?
119             $boolean = scalar(%$tiedhref);
120             $boolean = $xhash->scalar();
121              
122             ## Array-like operations
123              
124             $value = $xhash->pop(); # last value
125             ($key, $value) = $xhash->pop(); # last key/value
126             $value = $xhash->shift(); # first value
127             ($key, $value) = $xhash->shift(); # first key/value
128              
129             # Append values or { keys => values }
130             $xhash->push(@elements);
131             $xhash->pushref(\@elements, %options);
132              
133             # Insert values or { keys => values }
134             $xhash->unshift(@elements);
135             $xhash->unshiftref(\@elements, %options);
136              
137             # Merge in other XHashes (recursively)
138             $xhash->merge(\%options?, @xhashes);
139              
140             # Export in array-like fashion
141             @list = $xhash->as_array(%options);
142             $list = $xhash->as_arrayref(%options);
143              
144             # Export in hash-like fasion
145             @list = $xhash->as_hash(%options);
146             $list = $xhash->as_hashref(%options);
147              
148             # Reorder elements
149             $xhash->reorder($reference, @keys); # [] = sorted index_only
150              
151             # Remap elements
152             $xhash->remap(%mapping); # or \%mapping
153             $xhash->renumber(%options);
154              
155             ## TIEHASH methods - see perltie
156              
157             # TIEHASH, FETCH, STORE, CLEAR, DELETE, EXISTS
158             # FIRSTKEY, NEXTKEY, UNTIE, DESTROY
159              
160             =head1 DESCRIPTION
161              
162             Data::XHash provides an object-oriented interface to tied, ordered
163             hashes. Hash elements may be assigned keys explicitly or automatically
164             in mix-and-match fashion like arrays in PHP.
165              
166             It also includes support for trees of nested XHashes, tree traversal,
167             and conversion to and from native Perl data structures.
168              
169             Suggested uses include structured configuration information or HTTP query
170             parameters in which order may at least sometimes be significant, for
171             passing mixed positional and named parameters, sparse arrays, or porting
172             PHP code.
173              
174             =head1 EXPORTS
175              
176             You may export any of the shortcut functions. None are exported by default.
177              
178             =head1 FUNCTIONS
179              
180             =head2 $tiedref = xh(@elements)
181              
182             =head2 $tiedref = xhash(@elements)
183              
184             =head2 $tiedref = xhashref(\@elements, %options)
185              
186             =head2 $tiedref = xhn(@elements)
187              
188             =head2 $tiedref = xhr(\@elements, %options)
189              
190             =head2 $tiedref = xhrn(\@elements, %options)
191              
192             These convenience functions call C<< Data::XHash->new() >> and then
193             C the specified elements. The "r" and "ref" versions take an
194             arrayref of elements; the others take a list. The "n" versions are
195             shortcuts for the C<< nested => 1 >> option of C.
196              
197             $tiedref = xh('hello', {root=>xh({leaf=>'value'}),
198             {list=>xh(1, 2, 3)});
199             $tiedref = xhn('hello', {root=>{leaf=>'value'}},
200             {list=>[1, 2, 3]});
201              
202             =cut
203              
204 37     37 1 4674 sub xh { return __PACKAGE__->new()->pushref(\@_); }
205              
206 2     2 1 7 sub xhn { return __PACKAGE__->new()->pushref(\@_, nested => 1); }
207              
208 6     6 1 18 sub xhr { return __PACKAGE__->new()->pushref(@_); }
209              
210 0     0 1 0 sub xhrn { return __PACKAGE__->new()->pushref(shift, nested => 1, @_); }
211              
212             *xhash = \&xh;
213             *xhashref = \&xhr;
214              
215             =head1 METHODS
216              
217             =head2 Data::XHash->new( )
218              
219             =head2 $xhash->new( )
220              
221             These create a new Data::XHash object and tie it to a new, empty hash. They
222             bless the hash as well and return a reference to the hash (C<$tiedref>).
223              
224             Do not use C<< tie %some_hash, 'Data::XHash'; >> - it will croak!
225              
226             =cut
227              
228             sub new {
229 66     66 1 4388 my $type = shift;
230             # Support $xhash->new() for same-class auto-vivification.
231 66   66     370 my $class = blessed($type) || $type;
232 66         219 my $self = bless { }, $class; # The XHash object
233 66         102 my %hash;
234              
235 66         173 $self->clear();
236 66         274 tie %hash, $class, $self;
237 66         425 return bless \%hash, $class; # The XHash tiedref
238             }
239              
240             sub TIEHASH {
241 66     66   114 my ($class, $self) = @_;
242              
243 66 50       177 croak("Use \"${class}->new()\", not \"tie \%hash, '$class'\"") unless $self;
244 66         169 return $self;
245             }
246              
247             =head2 $tiedref->{$key}
248              
249             =head2 $tiedref->{\@path}
250              
251             =head2 $xhash->fetch($key)
252              
253             =head2 $xhash->fetch(\@path)
254              
255             These return the value for the specified hash key, or C if the key does
256             not exist.
257              
258             If the key parameter is reference to a non-empty array, its elements are
259             traversed as a path through nested XHashes.
260              
261             If the last path element is a hashref, the path will be auto-vivified
262             (Perl-speak for "created when referenced") and made to be an XHash if
263             necessary (think "fetch a path to a hash"). Otherwise, any missing
264             element along the path will cause C to be returned.
265              
266             $xhash->{[]}; # undef
267              
268             $xhash->{[qw/some path/, {}]}->isa('Data::XHash'); # always true
269             # Similar to native Perl: $hash->{some}{path} ||= {};
270              
271             =cut
272              
273             sub FETCH {
274 39     39   1260 my ($self, $key) = @_;
275              
276 39 100 100     133 if (ref($key) eq 'ARRAY' && @$key) {
277             # Fetch with path traversal
278 4         11 return $self->traverse($key, op => 'fetch')->{value};
279             }
280              
281             # Local fetch
282 35   66     114 $self = tied(%$self) || $self;
283 35         76 my $entry = $self->{hash}{$key};
284 35 100       137 return $entry? $entry->[2]: undef;
285             }
286              
287             *fetch = \&FETCH;
288              
289             =head2 $tiedref->{$key} = $value
290              
291             =head2 $tiedref->{\@path} = $value
292              
293             =head2 $xhash->store($key, $value, %options)
294              
295             =head2 $xhash->store(\@path, $value, %options)
296              
297             These store the value for the specified key in the XHash. Any existing value
298             for the key is overwritten. New keys are stored at the end of the XHash.
299              
300             If the key parameter is a reference to a non-empty array, its elements are
301             traversed as a path through nested XHashes. Path elements will be
302             auto-vivified as necessary and intermediate ones will be forced to XHashes.
303              
304             If the key is an empty path or the C value, or any path key is the
305             C value, the next available non-negative integer index in the
306             corresponding XHash is used instead.
307              
308             These return the XHash tiedref or object (whichever was used).
309              
310             Options:
311              
312             =over
313              
314             =item nested => $boolean
315              
316             If this option is true, arrayref and hashref values will be converted into
317             XHashes.
318              
319             =back
320              
321             =cut
322              
323             sub STORE {
324 203     203   443 my ($this, $key, $value, %options) = @_;
325 203         339 my $array_key = ref($key) eq 'ARRAY';
326              
327 203 100 100     832 if ($array_key && @$key) {
328             # Store with path traversal.
329 3         8 my $path = $this->traverse($key, op => 'store');
330 3         9 $path->{container}->store($path->{key}, $value, %options);
331             } else {
332             # Store locally.
333 200   66     718 my $self = tied(%$this) || $this;
334              
335             # Get the next index for undef or [].
336 200 100 100     1185 $key = defined($self->{max_index})? ($self->{max_index} + 1):
    100          
337             $self->next_index() if !defined($key) || $array_key;
338              
339 200 100       999 if ($options{nested}) {
340             # Convert nested native structures to XHashes.
341 14 100       44 if (ref($value) eq 'HASH') {
    100          
342 1         4 $value = $self->new()->pushref([$value], %options);
343             } elsif (ref($value) eq 'ARRAY') {
344 3         6 $value = $self->new()->pushref($value, %options);
345             }
346             }
347              
348 200 100       546 if (my $entry = $self->{hash}{$key}) {
349             # Replace the value for an existing key.
350 8         21 $entry->[2] = $value;
351             } else {
352 192         1230 my $link;
353 192 100       434 if (my $tail = $self->{tail}) {
354 131         215 my $head = $tail->[1];
355             # Link an additional element into a non-empty ring.
356 131         590 $link = $self->{hash}{$key} =
357             $tail->[1] = $head->[0] = [$tail, $head, $value, $key];
358             } else {
359             # Start a new key ring.
360 61         242 $link = $self->{hash}{$key} = [undef, undef, $value, $key];
361 61         141 $link->[0] = $link->[1] = $link;
362             }
363 192         428 $self->{tail} = $link;
364 192 100 66     3086 $self->{max_index} = $key
    100          
365             if ($key =~ /^\d+$/ && (defined($self->{max_index})?
366             ($key > $self->{max_index}): ($key >= $self->next_index())));
367             }
368             }
369              
370 203         2248 return $this;
371             }
372              
373             *store = \&STORE;
374              
375             =head2 %$tiedref = ()
376              
377             =head2 $xhash->clear( )
378              
379             These clear the XHash.
380              
381             Clear returns the XHash tiedref or object (whichever was used).
382              
383             =cut
384              
385             sub CLEAR {
386 215     215   1126 my ($this) = @_;
387 215   66     899 my $self = tied(%$this) || $this;
388              
389 215 100       552 if ($self->{hash}) {
390             # Blow away unweakened refs before tossing the hash.
391 149         182 @$_ = () foreach (values %{$self->{hash}});
  149         2176  
392             }
393 215         467 $self->{hash} = {};
394 215         1670 delete $self->{tail};
395 215         281 delete $self->{iter};
396 215         338 $self->{max_index} = -1;
397 215         1286 return $this;
398             }
399              
400             *clear = \&CLEAR;
401              
402             =head2 delete $tiedref->{$key} # or \@path
403              
404             =head2 $xhash->delete($key) # or \@path
405              
406             =head2 $xhash->delete(\%options?, @keys)
407              
408             These remove the element with the specified key and return its value. They
409             quietly return C if the key does not exist.
410              
411             The method call can also delete (and return) multiple local (not path) keys
412             at once.
413              
414             Options:
415              
416             =over
417              
418             =item to => $destination
419              
420             If C<$destination> is an arrayref, hashref, or XHash, each deleted
421             C<< { $key => $value } >> is added to it and the destination is returned
422             instead of the most recently deleted value.
423              
424             =back
425              
426             =cut
427              
428             sub DELETE : method {
429 48     48   83 my $self = shift;
430 48 100       144 my %options = ref($_[0]) eq 'HASH'? %{+shift}: ();
  12         64  
431 48         145 my $key = $_[0];
432              
433 48 50 33     152 if (ref($key) eq 'ARRAY' && @$key) {
434             # Delete across the path.
435 0         0 my $path = $self->traverse($key, op => 'delete');
436              
437 0 0       0 return $path->{container}?
438             $path->{container}->delete($path->{key}): undef;
439             }
440              
441             # Delete locally.
442 48         77 my $to = $options{to};
443 48         58 my $return;
444 48   66     1602 $self = tied(%$self) || $self;
445              
446 48         130 while (@_) {
447 63         87 $key = shift;
448              
449 63 100       209 if (my $link = $self->{hash}{$key}) {
450 61 100 66     421 if (ref($to) eq 'ARRAY') {
    100          
    100          
451 4         15 push(@$to, { $key => $link->[2] });
452             } elsif (ref($to) eq 'HASH') {
453 2         6 $to->{$key} = $link->[2];
454             } elsif (blessed($to) && $to->isa(__PACKAGE__)) {
455 20         65 $to->store($key, $link->[2]);
456             } else {
457 35         68 $return = $link->[2];
458             }
459              
460 61 100       164 if ($link->[0] != $link) {
461             # There are other keys, so unlink this one from the ring.
462 47         89 $link->[0][1] = $link->[1]; # prev.next = my.next
463 47         140 $link->[1][0] = $link->[0]; # next.prev = my.prev
464 47 100 100     248 $self->{max_index} = undef
465             if defined($self->{max_index}) && $self->{max_index} eq $key;
466 47 100       157 $self->{tail} = $link->[0] if $self->{tail} == $link;
467 47         346 delete $self->{hash}{$key};
468             } else {
469             # We're deleting the last key, so do a full reset.
470 14         32 $self->clear();
471             }
472             }
473             }
474              
475 48 100       808 return $to? $to: $return;
476             }
477              
478             *delete = \&DELETE;
479              
480             =head2 exists $tiedref->{$key} # or \@path
481              
482             =head2 $xhash->exists($key) # or \@path
483              
484             These return true if the key (or path) exists.
485              
486             =cut
487              
488             sub EXISTS {
489 14     14   24 my ($self, $key) = @_;
490              
491 14 50 33     40 if (ref($key) eq 'ARRAY' && @$key) {
492             # Check existence across the path.
493 0         0 my $path = $self->traverse($key, op => 'exists');
494              
495 0   0     0 return $path->{container} && $path->{container}->exists($path->{key});
496             }
497              
498             # Check existence locally.
499 14   66     50 $self = tied(%$self) || $self;
500 14         72 return exists($self->{hash}{$key});
501             }
502              
503             *exists = \&EXISTS;
504              
505             =head2 $xhash->FIRSTKEY( )
506              
507             This returns the first key (or C if the XHash is empty) and resets
508             the internal iterator.
509              
510             =cut
511              
512             sub FIRSTKEY {
513 3     3   18 my ($self) = @_;
514 3   66     69 $self = tied(%$self) || $self;
515              
516 3 50       14 if ($self->{tail}) {
517             # The first key is in the head (the tail's next link).
518 3         12 my $head = $self->{iter} = $self->{tail}[1];
519 3         18 return $head->[3];
520             }
521              
522 0         0 delete $self->{iter};
523 0         0 return undef;
524             }
525              
526             =head2 $xhash->first_key( )
527              
528             This returns the first key (or C if the XHash is empty).
529              
530             =cut
531              
532             sub first_key {
533 1     1   3 my ($self) = @_;
534 1   33     6 $self = tied(%$self) || $self;
535              
536 1 50       10 return ($self->{tail}? $self->{tail}[1][3]: undef);
537             }
538              
539             =head2 $xhash->previous_key($key)
540              
541             This returns the key before C<$key>, or C if C<$key> is the first
542             key or doesn't exist.
543              
544             =cut
545              
546             sub previous_key {
547 3     3 1 8 my ($self, $key) = @_;
548 3   33     11 $self = tied(%$self) || $self;
549              
550 3         9 my $entry = $self->{hash}{$key};
551 3 100 66     32 return (($entry && $entry != $self->{tail}[1])? $entry->[0][3]: undef);
552             }
553              
554             =head2 $xhash->NEXTKEY( )
555              
556             This returns the next key using the internal iterator, or C if there
557             are no more keys.
558              
559             =cut
560              
561             sub NEXTKEY {
562 9     9   15 my ($self) = @_;
563 9   66     36 $self = tied(%$self) || $self;
564              
565 9         18 my $iter = $self->{iter};
566 9 100 66     58 if ($iter && $iter != $self->{tail}) {
567 6         14 $iter = $self->{iter} = $iter->[1];
568 6         32 return $iter->[3];
569             }
570              
571 3         28 return undef;
572             }
573              
574             =head2 $xhash->next_key($key)
575              
576             This returns the key after C<$key>, or C if C<$key> is the last key or
577             doesn't exist.
578              
579             Path keys are not supported.
580              
581             =cut
582              
583             sub next_key {
584 0     0   0 my ($self, $key) = @_;
585 0   0     0 $self = tied(%$self) || $self;
586              
587 0         0 my $entry = $self->{hash}{$key};
588 0 0 0     0 return (($entry && $entry != $self->{tail})? $entry->[1][3]: undef);
589             }
590              
591             =head2 $xhash->last_key( )
592              
593             This returns the last key, or C if the XHash is empty.
594              
595             =cut
596              
597             sub last_key {
598 1     1 1 3 my $self = shift;
599 1   33     6 $self = tied(%$self) || $self;
600              
601 1 50       15 return ($self->{tail}? $self->{tail}[3]: undef);
602             }
603              
604             =head2 $xhash->next_index( )
605              
606             This returns the next numeric insertion index. This is either "0" or one more
607             than the current largest non-negative integer index.
608              
609             =cut
610              
611             sub next_index {
612 19     19 1 34 my ($self) = @_;
613 19   66     67 $self = tied(%$self) || $self;
614              
615 19 100       54 if (!defined($self->{max_index})) {
616             # Recalculate max_index if that key was previously deleted.
617 3         8 $self->{max_index} = -1;
618 3         6 foreach (grep(/^\d+$/, keys %{$self->{hash}})) {
  3         36  
619 6 50       33 $self->{max_index} = $_ if $_ > $self->{max_index};
620             }
621             }
622              
623 19         68 return $self->{max_index} + 1;
624             }
625              
626             =head2 scalar(%$tiedref)
627              
628             =head2 $xhash->scalar( )
629              
630             This returns true if the XHash is not empty.
631              
632             =cut
633              
634             sub SCALAR : method {
635 2     2   5 my ($self) = @_;
636              
637 2         14 return defined($self->{tail});
638             }
639              
640             *scalar = \&SCALAR;
641              
642             =head2 $xhash->keys(%options)
643              
644             This method is equivalent to C but may be called on the
645             object (and is much faster).
646              
647             Options:
648              
649             =over
650              
651             =item index_only => $boolean
652              
653             If true, only the integer index keys are returned. If false, all keys are
654             returned,
655              
656             =item sorted => $boolean
657              
658             If index_only mode is true, this option determines whether index keys are
659             returned in ascending order (true) or XHash insertion order (false).
660              
661             =back
662              
663             =cut
664              
665             sub keys : method {
666 18     18 1 46 my ($self, %options) = @_;
667 18   33     64 $self = tied(%$self) || $self;
668 18         26 my @keys;
669              
670 18 100       57 if (my $tail = $self->{tail}) {
671 17         24 my $link = $tail;
672 17         39 do {
673 75         109 $link = $link->[1];
674 75         341 push(@keys, $link->[3]);
675             } while ($link != $tail);
676             }
677              
678 18 100       54 if ($options{index_only}) {
679 5         63 @keys = grep(/^-?\d+$/, @keys);
680 5 100       33 @keys = sort({ $a <=> $b } @keys) if $options{sorted};
  20         30  
681             }
682              
683 18         114 return @keys;
684             }
685              
686             =head2 $xhash->values(\@keys?)
687              
688             This method is equivalent to C but may be called on the
689             object (and, if called without specific keys, is much faster too).
690              
691             You may optionally pass a reference to an array of keys whose values should
692             be returned (equivalent to the slice C<@{$tiedref}{@keys}>). Key paths are
693             allowed, but don't forget that the list of keys/paths must be provided as
694             an array ref (C<< [ $local_key, \@path ] >>).
695              
696             =cut
697              
698             sub values : method {
699 2     2 1 7 my $self = shift;
700 2         3 my $keys = shift;
701              
702 2   33     9 $self = tied(%$self) || $self;
703 2 100       8 if (ref($keys) eq 'ARRAY') {
704 1 50 50     24 return map(ref($_)? $self->fetch($_): ($self->{hash}{$_} || [])->[2],
705             @$keys);
706             }
707              
708 1         2 my @values;
709              
710 1 50       4 if (my $tail = $self->{tail}) {
711 1         2 my $link = $tail;
712 1         2 do {
713 3         4 $link = $link->[1];
714 3         12 push(@values, $link->[2]);
715             } while ($link != $tail);
716             }
717              
718 1         11 return @values;
719             }
720              
721             =head2 $xhash->foreach(\&coderef, @more_args)
722              
723             This method calls the coderef as follows
724              
725             push(@results, &$coderef($xhash, $key, $value, @more_args));
726              
727             once for each key/value pair in the XHash (if any), followed by a
728             call with both set to C.
729              
730             It returns the accumulated list of coderef's return values.
731              
732             Example:
733              
734             # The sum and product across an XHash of numeric values
735             %results = $xhash->foreach(sub {
736             my ($xhash, $key, $value, $calc) = @_;
737              
738             return %$calc unless defined($key);
739             $calc->{sum} += $value;
740             $calc->{product} *= $value;
741             return ();
742             }, { sum => 0, product => 1 });
743              
744             =cut
745              
746             sub foreach : method {
747 12     12 1 19 my $self = shift;
748 12         15 my $code = shift;
749 12         15 my @results;
750              
751 12   33     38 $self = tied(%$self) || $self;
752 12 50       36 if (my $tail = $self->{tail}) {
753 12         16 my $link = $tail;
754              
755 12         14 do {
756 31         81 $link = $link->[1];
757 31         79 push(@results, &$code($self, $link->[3], $link->[2], @_));
758             } while ($link != $tail);
759             }
760              
761 12         58 push(@results, &$code($self, undef, undef, @_));
762 12         103 return @results;
763             }
764              
765 0     0   0 sub UNTIE {}
766              
767 132     132   7867 sub DESTROY { shift->clear(); }
768              
769             =head2 $xhash->pop( )
770              
771             =head2 $xhash->shift( )
772              
773             These remove the first element (shift) or last element (pop) from the XHash
774             and return its value (in scalar context) or its key and value (in list
775             context). If the XHash was already empty, C or C<()> is returned
776             instead.
777              
778             =cut
779              
780             sub pop : method {
781 11     11 1 28 my ($self) = @_;
782              
783 11   33     33 $self = tied(%$self) || $self;
784 11 100       54 return wantarray? (): undef unless $self->{tail};
    100          
785              
786 8         15 my $key = $self->{tail}[3];
787 8 100       30 return wantarray? ($key, $self->delete($key)): $self->delete($key);
788             }
789              
790             sub shift : method {
791 11     11 1 35 my ($self) = @_;
792              
793 11   33     36 $self = tied(%$self) || $self;
794 11 100       55 return wantarray? (): undef unless $self->{tail};
    100          
795              
796 8         18 my $key = $self->{tail}[1][3];
797 8 100       29 return wantarray? ($key, $self->delete($key)): $self->delete($key);
798             }
799              
800             =head2 $xhash->push(@elements)
801              
802             =head2 $xhash->pushref(\@elements, %options)
803              
804             =head2 $xhash->unshift(@elements)
805              
806             =head2 $xhash->unshiftref(\@elements, %options)
807              
808             These append elements at the end of the XHash (C and C)
809             or insert elements at the beginning of the XHash (C and
810             C).
811              
812             Scalar elements are automatically assigned a numeric index using
813             C. Hashrefs are added as key/value pairs. References
814             to references are dereferenced by one level before being added. (To add
815             a hashref as a hashref rather than key/value pairs, push or unshift a
816             reference to the hashref instead.)
817              
818             These return the XHash tiedref or object (whichever was used).
819              
820             Options:
821              
822             =over
823              
824             =item at_key => $key
825              
826             This will push after C<$key> instead of at the end of the XHash or unshift
827             before C<$key> instead of at the beginning of the XHash. This only applies
828             to the first level of a nested push or unshift.
829              
830             This must be a local key (not a path), and the operation will croak if
831             the key is not found.
832              
833             =item nested => $boolean
834              
835             If true, values that are arrayrefs (possibly containing hashrefs) or
836             hashrefs will be recursively converted to XHashes.
837              
838             =back
839              
840             =cut
841              
842 4     4 1 21 sub push : method { return shift->pushref(\@_); }
843              
844             sub pushref {
845 67     67 1 206 my ($this, $list, %options) = @_;
846 67   66     375 my $self = tied(%$this) || $this;
847 67         128 my $at_key = delete $options{at_key};
848 67         79 my $save_tail;
849              
850 67 50       369 croak "pushref requires an arrayref" unless ref($list) eq 'ARRAY';
851              
852 67 100       164 if (defined($at_key)) {
853 7         16 my $entry = $self->{hash}{$at_key};
854 7 50       70 croak "pushref at_key => key does not exist" unless $entry;
855 7 100       30 if ($entry != $self->{tail}) {
856             # Temporarily shift the end of the ring
857 2         3 $save_tail = $self->{tail};
858 2         5 $self->{tail} = $entry;
859             }
860             }
861              
862 67         147 foreach my $item (@$list) {
863 144 100       753 if (ref($item) eq 'HASH') {
    50          
864 71         362 $self->store($_, $item->{$_}, %options) foreach (keys %$item);
865             } elsif (ref($item) eq 'REF') {
866 0         0 $self->store(undef, $$item, %options, nested => 0);
867             } else {
868 73         206 $self->store(undef, $item, %options);
869             }
870             }
871              
872             # Restore the ring after an at_key push.
873 67 100       587 $self->{tail} = $save_tail if $save_tail;
874              
875 67         330 return $this;
876             }
877              
878 2     2 1 165 sub unshift : method { return shift->unshiftref(\@_); }
879              
880             sub unshiftref {
881 7     7 1 185 my ($this, $list, %options) = @_;
882 7   66     34 my $self = tied(%$this) || $this;
883 7         15 my $at_key = delete($options{at_key});
884              
885 7 50       32 croak "unshiftref requires an arrayref" unless ref($list) eq 'ARRAY';
886              
887 7         204 my $save_tail = $self->{tail};
888              
889 7 100       23 if (defined($at_key)) {
890 2         6 my $entry = $self->{hash}{$at_key};
891 2 50       20 croak "unshiftref at_key => key does not exist"
892             unless $self->{hash}{$at_key};
893             # Temporarily shift the ring
894 2         5 $self->{tail} = $entry->[0];
895             }
896              
897 7         27 $self->pushref($list, %options);
898 7 100       29 $self->{tail} = $save_tail if $save_tail;
899              
900 7         38 return $this;
901             }
902              
903             =head2 $xhash->merge(\%options?, @xhashes)
904              
905             This recursively merges each of the XHash trees in C<@xhashes> into the
906             current XHash tree C<$xhash> as follows:
907              
908             If a key has both existing and new values and both are XHashes, the elements
909             in the new XHash are added to the existing XHash.
910              
911             Otherwise, if the new value is an XHash, the value is set to a B of
912             the new XHash.
913              
914             Otherwise the value is set to the new value.
915              
916             Returns the XHash tiedref or object (whichever was used).
917              
918             Examples:
919              
920             # Clone a tree of nested XHashes (preserving index keys)
921             $clone = xh()->merge({ indexed_as => 'hash' }, $xhash);
922              
923             # Merge $xhash2 (with new keys) into existing XHash $xhash1
924             $xhash1->merge($xhash2);
925              
926             Options:
927              
928             =over
929              
930             =item indexed_as => $type
931              
932             If C<$type> is C (the default), numerically-indexed items in
933             each merged XHash are renumbered as they are added (like
934             C<< push($xhash->as_array()) >>).
935              
936             If C<$type> is C, numerically-indexed items are merged without
937             renumbering (like C<< push($xhash->as_hash()) >>).
938              
939             =back
940              
941             =cut
942              
943             sub merge {
944 8     8 1 13 my $self = shift;
945 8 100       27 my %options = (ref($_[0]) eq 'HASH')? %{shift()}: ();
  7         24  
946              
947 8   100     27 $options{'indexed_as'} ||= 'array';
948 8         15 foreach (@_) {
949             $_->foreach(sub {
950 35     35   58 my ($xhash, $key, $new_val) = @_;
951 35         39 my $cur_val;
952              
953 35 100       88 return () unless defined($key);
954 25 100 100     123 if ($options{'indexed_as'} ne 'hash' && $key =~ /^-?\d+$/) {
955             # Renumber index keys in array mode
956 9         20 $key = $self->next_index();
957 9         53 $cur_val = undef;
958             } else {
959 16         45 $cur_val = $self->fetch($key);
960             }
961 25 100 66     138 if (blessed($new_val) && $new_val->isa(__PACKAGE__)) {
962 5 100 66     37 $self->store($key, $cur_val = $new_val->new())
963             unless blessed($cur_val) && $cur_val->isa(__PACKAGE__);
964 5         18 $cur_val->merge(\%options, $new_val);
965             } else {
966 20         43 $self->store($key, $new_val);
967             }
968              
969 25         98 return ();
970 10         72 });
971             }
972              
973 8         24 return $self;
974             }
975              
976             =head2 $xhash->as_array(%options)
977              
978             =head2 $xhash->as_arrayref(%options)
979              
980             =head2 $xhash->as_hash(%options)
981              
982             =head2 $xhash->as_hashref(%options)
983              
984             These methods export the contents of the XHash as native Perl arrays or
985             arrayrefs.
986              
987             The "array" versions return the elements in an "array-like" array or array
988             reference; elements with numerically indexed keys are returned without their
989             keys.
990              
991             The "hash" versions return the elements in an "hash-like" array or array
992             reference; all elements, including numerically indexed ones, are returned
993             with keys.
994              
995             xh( { foo => 'bar' }, 123, \{ key => 'value' } )->as_arrayref();
996             # [ { foo => 'bar' }, 123, \{ key => 'value'} ]
997              
998             xh( { foo => 'bar' }, 123, \{ key => 'value' } )->as_hash();
999             # ( { foo => 'bar' }, { 0 => 123 }, { 1 => { key => 'value' } } )
1000              
1001             xh(xh({ 3 => 'three' }, { 2 => 'two' })->as_array())->as_hash();
1002             # ( { 0 => 'three' }, { 1 => 'two' } )
1003              
1004             xh( 'old', { key => 'old' } )->push(
1005             xh( 'new', { key => 'new' } )->as_array())->as_array();
1006             # ( 'old', { key => 'new' }, 'new' )
1007              
1008             xh( 'old', { key => 'old' } )->push(
1009             xh( 'new', { key => 'new' } )->as_hash())->as_hash();
1010             # ( { 0 => 'new' }, { key => 'new' } )
1011              
1012             Options:
1013              
1014             =over
1015              
1016             =item nested => $boolean
1017              
1018             If this option is true, trees of nested XHashes are recursively expanded.
1019              
1020             =back
1021              
1022             =cut
1023              
1024 0     0 1 0 sub as_array { return @{shift->as_arrayref(@_)}; }
  0         0  
1025              
1026             sub as_arrayref {
1027 2     2 1 5 my ($self, %options) = @_;
1028 2   33     7 $self = tied(%$self) || $self;
1029 2         3 my $tail = $self->{tail};
1030              
1031 2 50       6 return [] unless $tail;
1032              
1033 2         3 my (@list, $key, $value);
1034 2         3 my $link = $tail;
1035 2         3 do {
1036 5         9 $link = $link->[1];
1037 5         6 ($key, $value) = @{$link}[3, 2];
  5         15  
1038              
1039 5 100       21 if ($key =~ /^-?\d+$/) {
1040 2 50 33     18 if ($options{nested} && blessed($value) &&
      33        
1041             $value->isa(__PACKAGE__)) {
1042 0         0 push(@list, $value->as_arrayref(%options));
1043             } else {
1044 2 50       15 push(@list, ref($value) =~ /HASH|REF/? \$value: $value);
1045             }
1046             } else {
1047 3 100 66     28 if ($options{nested} && blessed($value) &&
      66        
1048             $value->isa(__PACKAGE__)) {
1049 1         16 push(@list, { $key => $value->as_arrayref(%options) });
1050             } else {
1051 2         16 push(@list, { $key => $value });
1052             }
1053             }
1054             } while ($link != $self->{tail});
1055              
1056 2         18 return \@list;
1057             }
1058              
1059 0     0 1 0 sub as_hash { return @{shift->as_hashref(@_)}; }
  0         0  
1060              
1061             sub as_hashref {
1062 63     63 1 645 my ($self, %options) = @_;
1063 63   33     180 $self = tied(%$self) || $self;
1064 63         105 my $tail = $self->{tail};
1065              
1066 63 100       188 return [] unless $tail;
1067              
1068 60         75 my (@list, $key, $value);
1069 60         79 my $link = $tail;
1070 60         100 do {
1071 190         290 $link = $link->[1];
1072 190         237 ($key, $value) = @{$link}[3, 2];
  190         533  
1073              
1074 190 100 100     851 if ($options{nested} && blessed($value) && $value->isa(__PACKAGE__)) {
      66        
1075 9         39 push(@list, { $key => $value->as_hashref(%options) });
1076             } else {
1077 181         842 push(@list, { $key => $value });
1078             }
1079             } while ($link != $tail);
1080              
1081 60         621 return \@list;
1082             }
1083              
1084             =head2 $xhash->reorder($refkey, @keys)
1085              
1086             This reorders elements within the XHash relative to the reference element
1087             having key C<$refkey>, which must exist and will not be moved.
1088              
1089             If the reference key appears in C<@keys>, the elements with keys preceding
1090             it will be moved immediately before the reference element. All other
1091             elements will be moved immediately following the reference element.
1092              
1093             Only the first occurence of any given key in C<@keys> is
1094             considered - duplicates are ignored.
1095              
1096             If any key is an arrayref, it is replaced with a sorted list of index keys.
1097              
1098             This method returns the XHash tiedref or object (whichever was used).
1099              
1100             # Move some keys to the beginning of the XHash.
1101             $xhash->reorder($xhash->first_key(), @some_keys,
1102             $xhash->first_key());
1103              
1104             # Move some keys to the end of the XHash.
1105             $xhash->reorder($xhash->last_key(), @some_keys);
1106              
1107             # Group numeric index keys in ascending order at the lowest one.
1108             $xhash->reorder([]);
1109              
1110             =cut
1111              
1112             sub reorder {
1113 4     4 1 14 my ($this, @keys) = @_;
1114 4   33     18 my $self = tied(%$this) || $this;
1115 4         8 my ($refkey, $before, @after);
1116              
1117 4 50       41 @keys = map(ref($_) eq 'ARRAY'?
1118             $self->keys(index_only => 1, sorted => 1): $_, @keys);
1119 4         9 $refkey = shift(@keys);
1120              
1121 4 50       19 croak("reorder reference key does not exist")
1122             unless $self->{hash}{$refkey};
1123              
1124 4         13 while (@keys) {
1125 16         28 my $key = shift(@keys);
1126              
1127 16 100       47 if ($key ne $refkey) {
    50          
1128 14 50       83 push(@after, { $key => $self->delete($key) })
1129             if $self->{hash}{$key};
1130             } elsif (!$before) {
1131 2         7 $before = [ @after ];
1132 2         7 @after = ();
1133             }
1134             }
1135              
1136 4 100       19 $self->unshiftref($before, at_key => $refkey) if $before;
1137 4 100       41 $self->pushref(\@after, at_key => $refkey) if @after;
1138              
1139 4         23 return $this;
1140             }
1141              
1142             =head2 $xhash->remap(\%mapping)
1143              
1144             =head2 $xhash->remap(%mapping)
1145              
1146             This remaps element keys according to the specified mapping (a hash of
1147             C<< $old_key => $new_key >>). The mapping must map old keys to new keys
1148             one-to-one.
1149              
1150             The order of elements in the XHash is unchanged.
1151              
1152             The XHash tiedref or object is returned (whichever was used).
1153              
1154             =cut
1155              
1156             sub remap {
1157 2     2 1 6 my $this = shift;
1158 2   33     8 my $self = tied(%$this) || $this;
1159 2 50       9 my %map = ref($_[0]) eq 'HASH'? %{$_[0]}: @_;
  2         12  
1160 2         6 my %hash;
1161              
1162 2         23 croak "remap mapping must be unique"
1163 2 50       3 unless keys(%{{ reverse %map }}) == keys(%map);
1164              
1165 2         8 my ($key, $new_key, $entry);
1166 2         4 while (($key, $entry) = each(%{$self->{hash}})) {
  14         52  
1167 12 50       44 $key = $entry->[3] = $new_key if defined($new_key = $map{$key});
1168 12         27 $hash{$key} = $entry;
1169             }
1170              
1171 2         5 $self->{hash} = \%hash;
1172              
1173 2         11 return $this;
1174             }
1175              
1176             =head2 $xhash->renumber(%options)
1177              
1178             This renumbers all elements with an integer index (those returned by
1179             C<< $xhash->keys(index_only => 1) >>). The order of elements is
1180             unchanged.
1181              
1182             It returns the XHash tiedref or object (whichever was used).
1183              
1184             Options:
1185              
1186             =over
1187              
1188             =item from => $starting_index
1189              
1190             Renumber from C<$starting_index> instead of the default zero.
1191              
1192             =item sorted => $boolean
1193              
1194             This option is passed to C<< $xhash->keys() >>.
1195              
1196             If set to true, keys will be renumbered in sorted sequence. This results
1197             in a "relative" renumbering (previously higher index keys will still be
1198             higher after renumbering, regardless of order in the XHash).
1199              
1200             If false or not set, keys will be renumbered in XHash (or "absolute") order.
1201              
1202             =back
1203              
1204             =cut
1205              
1206             sub renumber {
1207 2     2 1 11 my ($self, %options) = @_;
1208 2   50     14 my $start = $options{from} || 0;
1209              
1210 2         12 my @keys = $self->keys(index_only => 1, sorted => $options{sorted});
1211 2 50       9 if (@keys) {
1212 2         3 my %map;
1213              
1214 2         17 @map{@keys} = map($_ + $start, 0 .. $#keys);
1215 2         11 $self->remap(\%map);
1216             }
1217              
1218 2         12 return $self;
1219             }
1220              
1221             =head2 $xhash->traverse($path, %options?)
1222              
1223             This method traverses key paths across nested XHash trees. The path may be
1224             a simple scalar key, or it may be an array reference containing multiple
1225             keys along the path.
1226              
1227             An C value along the path will translate to the next available
1228             integer index at that level in the path. A C<{}> at the end of the path
1229             forces auto-vivification of an XHash at the end of the path if one does not
1230             already exist there.
1231              
1232             This method returns a reference to an hash containing the elements
1233             "container", "key", and "value". If the path does not exist, the container
1234             and key values with be C.
1235              
1236             An empty path (C<[]>) is equivalent to a path of C.
1237              
1238             Options:
1239              
1240             =over
1241              
1242             =item op
1243              
1244             This option specifies the operation for which the traversal is being
1245             performed (fetch, store, exists, or delete).
1246              
1247             =item xhash
1248              
1249             This forces the path to terminate with an XHash (for "fetch" paths ending in
1250             C<{}>).
1251              
1252             =item vivify
1253              
1254             This will auto-vivify missing intermediate path elements.
1255              
1256             =back
1257              
1258             =cut
1259              
1260             sub traverse {
1261 7     7 1 17 my ($self, $path, %options) = @_;
1262 7 50       23 my @path = (ref($path) eq 'ARRAY')? @$path: ($path);
1263 7         7 my $container = $self;
1264 7   50     16 my $op = $options{op} || '';
1265 7         10 my ($key, $value);
1266              
1267 7 100 66     34 if (@path && ref($path[-1]) eq 'HASH') {
1268             # Vivify to terminal XHash on fetch path [ ... {} ].
1269 2 50       11 $options{vivify} = $options{xhash} = 1 if $op eq 'fetch';
1270 2         4 pop(@path);
1271             }
1272              
1273             # Default to vivify on store.
1274 7 100 66     29 $options{vivify} = 1 if $op eq 'store' && !exists($options{vivify});
1275              
1276 7         13 while (@path) {
1277 12         18 $key = shift(@path);
1278 12 100 100     35 if (!defined($key) || !$container->exists($key)) {
1279             # This part of the path is missing. Stop or vivify.
1280 5 100       17 return { container => undef, key => undef, value => undef }
1281             unless $options{vivify};
1282              
1283             # Use the next available index for undef keys.
1284 4 100       21 $key = $container->next_index() unless defined($key);
1285              
1286 4 100 66     20 if (@path || $options{xhash}) {
1287             # Vivify an XHash for intermediates or fetch {}.
1288 1         3 $container->store($key, $value = $self->new());
1289             } else {
1290 3         5 $value = undef;
1291             }
1292             } else {
1293 7         15 $value = $container->fetch($key);
1294 7 50 100     177 $container->store($key, $value = $self->new())
      33        
      66        
1295             if (@path || $options{xhash}) &&
1296             (!blessed($value) || !$value->isa(__PACKAGE__));
1297             }
1298 11 100       38 $container = $value if @path;
1299             }
1300              
1301 6 50       13 $key = $container->next_index() unless defined($key);
1302 6         41 return { container => $container, key => $key, value => $value };
1303             }
1304              
1305             =head1 AUTHOR
1306              
1307             Brian Katzung, C<< >>
1308              
1309             =head1 BUG TRACKING
1310              
1311             Please report any bugs or feature requests to
1312             C, or through the web interface at
1313             L. I will be
1314             notified, and then you'll automatically be notified of progress on your
1315             bug as I make changes.
1316              
1317             =head1 SUPPORT
1318              
1319             You can find documentation for this module with the perldoc command.
1320              
1321             perldoc Data::XHash
1322              
1323             You can also look for information at:
1324              
1325             =over 4
1326              
1327             =item * RT: CPAN's request tracker (report bugs here)
1328              
1329             L
1330              
1331             =item * AnnoCPAN: Annotated CPAN documentation
1332              
1333             L
1334              
1335             =item * CPAN Ratings
1336              
1337             L
1338              
1339             =item * Search CPAN
1340              
1341             L
1342              
1343             =back
1344              
1345             =head1 SEE ALSO
1346              
1347             =over
1348              
1349             =item L
1350              
1351             An array wrapper to manage elements as key/value pairs.
1352              
1353             =item L
1354              
1355             Allows you to assign names to array indexes.
1356              
1357             =item L
1358              
1359             Like L, but with native Perl syntax.
1360              
1361             =item L
1362              
1363             An ordered map implementation, currently implementing an array of single-key
1364             hashes stored in key-sorting order.
1365              
1366             =item L
1367              
1368             Auto accessors and mutators for hashes and tied hashes.
1369              
1370             =item L
1371              
1372             A basic hash-of-hash traverser. Discovered by the author after writing
1373             Data::XHash.
1374              
1375             =item L
1376              
1377             An ordered hash implementation with a different interface and data
1378             structure and without auto-indexed keys and some of Data::XHash's
1379             other features.
1380              
1381             Tie::IxHash is probably the "standard" ordered hash module. Its
1382             simpler interface and underlying array-based implementation allow it to
1383             be almost 2.5 times faster than Data::XHash for some operations.
1384             However, its Delete, Shift, Splice, and Unshift methods degrade in
1385             performance with the size of the hash. Data::XHash uses a doubly-linked
1386             list, so its delete, shift, splice, and unshift methods are unaffected
1387             by hash size.
1388              
1389             =item L
1390              
1391             Hashes stored as arrays in key sorting-order.
1392              
1393             =item L
1394              
1395             A linked-list-based hash like L, but it doesn't support the
1396             push/pop/shift/unshift array interface and it doesn't have automatic keys.
1397              
1398             =item L
1399              
1400             Hashes with items stored in least-recently-used order.
1401              
1402             =back
1403              
1404             =for comment
1405             head1 ACKNOWLEDGEMENTS
1406              
1407             =head1 LICENSE AND COPYRIGHT
1408              
1409             Copyright 2012 Brian Katzung.
1410              
1411             This program is free software; you can redistribute it and/or modify it
1412             under the terms of either: the GNU General Public License as published
1413             by the Free Software Foundation; or the Artistic License.
1414              
1415             See http://dev.perl.org/licenses/ for more information.
1416              
1417             =cut
1418              
1419             1; # End of Data::XHash