File Coverage

blib/lib/Tie/Hash/MultiKey.pm
Criterion Covered Total %
statement 333 351 94.8
branch 81 104 77.8
condition 19 29 65.5
subroutine 30 33 90.9
pod 10 10 100.0
total 473 527 89.7


line stmt bran cond sub pod time code
1             package Tie::Hash::MultiKey;
2              
3             #use diagnostics;
4 23     23   22908 use strict;
  23         47  
  23         929  
5 23     23   129 use Carp;
  23         42  
  23         1858  
6 23     23   30323 use Tie::Hash;
  23         28965  
  23         838  
7 23     23   155 use vars qw($VERSION);
  23         42  
  23         97468  
8              
9             $VERSION = do { my @r = (q$Revision: 0.08 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
10              
11             my $indexmax = 2**48; # a really big unique number that perl will not convert to float
12              
13             =head1 NAME
14              
15             Tie::Hash::MultiKey - multiple keys per value
16              
17             =head1 SYNOPSIS
18              
19             use Tie::Hash::MultiKey;
20              
21             $thm = tie %hash, qw(Tie::Hash::MultiKey) ,@optionalext;
22             $thm = tied %hash;
23              
24             untie %hash;
25              
26             ($href,$thm) = new Tie::Hash::MultiKey;
27              
28             $hash{'foo'} = 'baz';
29             or
30             $hash{'foo', 'bar'} = 'baz';
31             or
32             $array_ref = ['foo', 'bar'];
33             $hash{ $array_ref } = 'baz';
34              
35             print $hash{foo}; # prints 'baz'
36             print $hash{bar}; # prints 'baz'
37              
38             $array_ref = ['fuz','zup'];
39             $val = tied(%hash)->addkey('fuz' => 'bar');
40             $val = tied(%hash)->addkey('fuz','zup' => 'bar');
41             $val = tied(%hash)->addkey( $array_ref => 'bar');
42              
43             print $hash{fuz} # prints 'baz'
44              
45             $array_ref = ['foo', 'bar'];
46             $val = tied(%hash)->remove('foo');
47             $val = tied(%hash)->remove('foo', 'bar');
48             $val = tied(%hash)->remove( $array_ref );
49              
50             $val = tied(%hash)->delkey(); alias for above
51              
52             @ordered_keys = tied(%hash)->keylist('foo')
53             @allkeys_by_order = tied(%hash)->keylist();
54             @slotlist = tied(%hash)->slotlist($i);
55             @ordered_vals = tied(%hash)->vals();
56              
57             $num_vals = tied(%hash)->size;
58             $num_vals = tied(%hash)->consolidate;
59              
60             ($newRef,$newThm) = tied(%hash)->clone();
61             $newThm = tied(%hash)->copy(tied(%new),@optionalext);
62              
63             All of the above methods can be accessed as:
64              
65             i.e. $thm->consolidate;
66              
67             =head1 DESCRIPTION
68              
69             Tie::Hash::MultiKey creates hashes that can have multiple ordered keys for a single value.
70             As shown in the SYNOPSIS, multiple keys share a common value.
71              
72             Additional keys can be added that share the same value and keys can be removed without deleting other
73             keys that share that value.
74              
75             STORE..ing a value for one or more keys that already exist will overwrite
76             the existing value and add any missing keys to the key group for that
77             value.
78              
79             B multiple key values supplied as an ARRAY to STORE and DELETE
80             operations are passed by Perl as a B string separated by Perl's $;
81             multidimensional array seperator. i.e.
82              
83             $hash{'a','b','c'} = $something;
84             or
85             @keys = ('a','b','c');
86             $hash{@keys} = $something'
87              
88             This really means $hash{join($;, 'a','b','c')};
89              
90             Tie::Hash::MultiKey will do the right thing as long as your keys B
91             contain binary data the may include the $; separator character.
92              
93             It is recommended that you use the ARRAY_REF construct to supply multiple
94             keys for binary data. i.e.
95              
96             $hash{['a','b','c']} = $something;
97             or
98             $keys = ['a','b','c'];
99             $hash{$keys} = $something;
100              
101             The ARRAY_REF construct is ALWAYS safe.
102              
103             =cut
104              
105             #
106             # data structure
107             # [
108             #
109             # 0 => { # $kh
110             # key => vi # value_index for array below
111             # },
112             # 1 => { # $vh
113             # vi => value, # contains value
114             # },
115             # 2 => { # $sh pointer to hash list of all shared keys
116             # vi = {key => dummy, key => dummy, ...}, values unused
117             # },
118             # 3 => vi, # numeric value of value index
119             # 4 => or, # numeric value of key order
120             # 5 => crumbs # STORE key value
121             # 6 => reserved
122             # 7 => { # extensions
123             # FETCH => subref, # required
124             # STORE => subref, # required
125             # DELETE => subref, # required
126             # COPY => subref, # required
127             # CLEAR => subref, # required
128             # REORDERV => subref, # required
129             # TIE => subref, # optional
130             # EXISTS => subref, # optional
131             # NEXT => subref, # optional
132             # ADDKEY => subref, # optional
133             # DELKEY => subref, # optional
134             # REORDERK => subref, # optional
135             # CONSOLD => subref, # optional
136             # one or more key names as required
137             # DATAn => scalar, array_ref, hash_ref
138             # }
139             # ]
140              
141             my @extrequired = qw(
142             FETCH
143             STORE
144             DELETE
145             COPY
146             CLEAR
147             REORDERV
148             );
149             my @extoptional = qw(
150             TIE
151             EXISTS
152             NEXT
153             ADDKEY
154             DELKEY
155             REORDERK
156             CONSOLD
157             );
158              
159             sub TIEHASH {
160 27     27   8769 my $class = shift;
161 27         132 my $self = bless [{},{},{},0,0,undef], $class;
162 27 100       156 if (@_) {
163 1 50       20 my %extensions = ref $_[0] ? @{$_[0]} : @_;
  0         0  
164 1         2 foreach (@extrequired) {
165 6 50       16 unless (exists $extensions{$_}) {
    50          
166 0         0 croak "missing required extension for '$_'";
167             } elsif (ref $extensions{$_} ne 'CODE') {
168 0         0 croak "'$_' extension pointer is not a subref";
169             } else {
170 6         13 $self->[7]->{$_} = $extensions{$_};
171             }
172             }
173              
174 1         2 foreach(@extoptional) {
175 7 50       17 unless (exists $extensions{$_}) {
    50          
176 0     0   0 $self->[7]->{$_} = sub {};
  0         0  
177             }
178             elsif (ref $extensions{$_} ne 'CODE') {
179 0         0 croak "'$_' extension pointer is not a subref";
180             } else {
181 7         14 $self->[7]->{$_} = $extensions{$_};
182             }
183             }
184              
185 1         4 $self->[7]->{TIE}->($self); # execute TIE extension to create DATA element
186             }
187 27         110 $self;
188             }
189              
190             # extract reference type and class from referrant or return an empty array
191             # class may be empty;
192             sub _ref_class {
193 7     7   13 my $src = shift;
194 7 100       24 my $ref = ref $src or return ();
195 4         5 my $class;
196 4 50       76 if ( "$src" =~ /^\Q$ref\E\=([A-Z]+)\(0x[0-9a-fA-Z]+\)$/ ) {
197 0         0 $class = $ref;
198 0         0 $ref = $1;
199             }
200 4         11 return ($ref,$class);
201             }
202              
203             sub _isarrayref {
204 7     7   19 my($ref,$class) = &_ref_class;
205 7 100 66     90 return ($ref && $ref eq 'ARRAY') ? 1:0;
206             }
207              
208             sub _wash {
209 117     117   167 my $keys = shift;
210 117 50       764 $keys = [$keys eq ''
    100          
211             ? ('')
212             : split /$;/, $keys, -1]
213             unless ref $keys eq 'ARRAY';
214 117 50       316 croak "empty key\n" unless @$keys;
215 117         324 return $keys;
216             }
217              
218             sub FETCH {
219 112     112   3688 my($self,$key) = @_;
220 112         108 my $okey = $key;
221             #
222             # in the case where an autoFETCH is done after a store
223             # i.e.
224             # $x = $hp->{[k1,k2,k3]} = item
225             # or $x = $hp->{ k1,k2,k3 } = item
226             #
227             # the key set is passed by perl to the fetch instead of one of the keys
228             #
229             # check if a fetch follows a store where
230             # 1 the key is an ARRAY and the referrant from the STORE are equal
231             # 2 the key, stringified is equal to the key from the STORE
232             #
233             # if either of these two condition are met, wash the keys and use
234             # key[0] as the FETCH key
235             #
236 112         150 my $crumbs = $self->[5];
237 112 100       187 if (defined $crumbs) { # see if a recent STORE left key crumbs
238 5         12 $self->[5] = undef; # yes, clear it
239 5 100 66     18 if ((_isarrayref($crumbs) &&
      66        
      100        
240             _isarrayref($key) && # keys are really ARRAY's
241             $key == $crumbs ) || # and referrants the same
242             $key . $; . 'X' eq $crumbs . $; . 'X') # or keys as string identical
243             {
244 4         5 $key = ${_wash($key)}[0];
  4         10  
245             }
246             }
247 112 50       209 return undef unless exists $self->[0]->{$key};
248 112         161 my $vi = $self->[0]->{$key}; # get key index
249 112 100       206 $self->[7]->{FETCH}->($self,$okey,$vi) if $self->[7]; # extend functionality ($vi)
250 112         395 return $self->[1]->{$vi};
251             }
252              
253             # take arguments of the form:
254             # $array_ref, $val
255             # or
256             # $a0, $a1, $a2, $val
257             # and returns
258             # $val, @aN
259              
260             sub _flip {
261 17     17   1763 my $val;
262 17 100       76 if (ref $_[0] eq "ARRAY") {
263 9         17 return ($_[1],@{$_[0]});
  9         41  
264             }
265 8         32 return (pop(@_),@_);
266             }
267              
268             sub STORE {
269 96     96   26042 my($self,$keys,$val) = @_;
270 96         167 $self->[5] = $keys;
271 96         114 my @keys = @{_wash($keys)};
  96         247  
272 96         149 my($kh,$vh,$sh) = @{$self};
  96         157  
273 96         111 my($vi,%found);
274 96         168 foreach my $key (@keys) {
275 218         224 my $vi;
276 218 100       549 next unless exists $kh->{$key};
277 6         13 $vi = $kh->{$key}; # get key index
278 6         17 $found{$vi} = $sh->{$vi}->{$key}; # capture shared key value
279             }
280 96         192 my @vi = keys %found;
281 96         155 $keys = {};
282 96         137 my $ostart = $self->[4];
283 96         135 my $oend = $ostart + $#keys; # first key order entry
284 96         187 $self->[4] = $oend + 1; # last key order entry
285 96         163 @{$keys}{@keys} = ($ostart..$oend); # create key list
  96         452  
286 96 100       220 if (@vi) { # if there are existing keys
287 6         12 foreach (@vi) { # consolidate keys
288 6         10 my @sk = keys %{$sh->{$_}}; # shared keys
  6         21  
289 6         12 @{$keys}{@sk} = @{$sh->{$_}}{@sk};
  6         15  
  6         14  
290 6         14 delete $vh->{$_}; # delete existing value
291 6         25 delete $sh->{$_}; # delete existing key list
292             }
293             } else {
294 90         214 $vi[0] = $self->[3]++; # new key pointer
295             }
296 96         140 $vi = shift @vi;
297              
298 96         259 $vh->{$vi} = $val; # set value
299 96         148 $sh->{$vi} = $keys; # set key list
300 96         246 foreach (keys %$keys) {
301 223         443 $kh->{$_} = $vi; # set value index
302             }
303 96 50       270 $self->_rordkeys() if $self->[3] > $indexmax;
304 96 50       221 $self->_rordvals() if $self->[4] > $indexmax;
305 96 100       216 $self->[7]->{STORE}->($self,\@keys,$vi) if $self->[7]; # extend functionality (value index)
306 96         467 $val;
307             }
308              
309             sub DELETE {
310 10     10   16551 my($self,$keys) = @_;
311 10         25 $self->[5] = undef; # clear crumbs
312 10         39 my @keys = @{_wash($keys)};
  10         28  
313 10         20 my($kh,$vh,$sh) = @{$self};
  10         26  
314 10         18 my @vis = delete @{$kh}{@keys}; # delete all identified keys
  10         34  
315 10         15 my(@dkeys,@vix);
316 10         22 foreach (@vis) { # $vi delete key shared list entries
317 16 100 66     96 unless (defined $_ && defined $sh->{$_}) { # already deleted?
318 2         3 $_ = ''; # vi is never empty
319 2         4 next;
320             }
321 14         48 push @vix, $_; # save unique value indices
322 14         26 my $keys = delete $sh->{$_};
323 14         65 @keys = sort { $keys->{$a} <=> $keys->{$b} } keys %$keys; # all keys in this key set in the order added
  43         86  
324 14         29 push @dkeys, @keys;
325 14         18 delete @{$kh}{@keys}; # delete remaining keys in key set
  14         53  
326             }
327 10 100       41 $self->[7]->{DELETE}->($self,\@dkeys,\@vix) if $self->[7];
328 10         30 delete @{$vh}{@vix}; # delete and return values in delete key order
  10         71  
329             } # NOTE: does not look like 'delete' does a wantarray
330              
331             sub EXISTS {
332 3     3   1071 $_[0]->[5] = undef; # clear crumbs
333 3 50       11 return undef unless exists $_[0]->[0]->{$_[1]};
334 3 50       15 $_[0]->[7]->{EXISTS}->(@_) if $_[0]->[7]; # ($key)
335 3         36 1;
336             }
337              
338             sub FIRSTKEY {
339 11     11   23315 keys %{$_[0]->[0]}; # reset iterator
  11         35  
340 11         40 &NEXTKEY;
341             }
342              
343             sub NEXTKEY {
344             # defined (my $key = each %{$_[0]->[0]}) or return undef;
345             # return $key;
346 128     128   277 $_[0]->[5] = undef; # clear crumbs
347 128         113 my($key,$vi) = each %{$_[0]->[0]};
  128         231  
348 128 100 100     330 $_[0]->[7]->{NEXT}->($_[0],$key,$vi) if $_[0]->[7] && defined $key;
349 128         444 $key;
350             }
351              
352             # delete all key, value sets
353             sub _clear {
354 12     12   20 my $self = shift;
355 12         22 $self->[3] = 0;
356 12         18 $self->[4] = 0;
357 12         25 $self->[5] = undef;
358 12         17 %{$self->[0]} = (); # empty existing hashes
  12         46  
359 12         17 %{$self->[1]} = ();
  12         32  
360 12         18 %{$self->[2]} = ();
  12         45  
361 12         24 $self;
362             }
363              
364             sub CLEAR {
365 9     9   8419 my $self = &_clear;
366 9 100       43 $self->[7]->{CLEAR}->($self) if $self->[7];
367 9         420 $self;
368             }
369              
370             sub SCALAR {
371 0     0   0 $_[0]->[5] = undef; # clear crumbs
372             # no extension
373 0         0 scalar %{$_[0]->[0]};
  0         0  
374             }
375              
376             =over 4
377              
378             =item * $thm = tie %hash,'Tie::Hash::MultiKey' ,%optional_ex
379              
380             Ties a %hash to this package for enhanced capability and returns a method
381             pointer.
382              
383             my %hash;
384             my $thm = tie %hash,'Tie::Hash::MultiKey';
385              
386             Extension of this module is discussed in detail below.
387              
388             =item * $thm = tied %hash;
389              
390             Returns a method pointer for this package.
391              
392             =item * untie %hash;
393              
394             Breaks the binding between a variable and this package. There is no affect
395             if the variable is not tied.
396              
397             B that if you have created a reference to the tied hash, untie
398             will not work until that binding is broken. This means that the object will
399             not be destroyed or garbage collected and the memory will not be reclaimed.
400              
401             i.e WRONG
402              
403             $thm = tie %h, 'Tie::Hash::MultiKey';
404             ... code ...
405             untie %h;
406              
407             RIGHT
408              
409             $thm = tie %h, 'Tie::Hash::MultiKey';
410             ... code ...
411             undef $thm;
412             untie %h;
413              
414             =item * ($href,$thm) = new 'Tie::Hash::MultiKey' ,%optional_ex
415              
416             This method returns an UNBLESSED reference to an anonymous tied %hash.
417              
418             input: none
419             returns: unblessed tied %hash reference,
420             object handle
421              
422             To get the object handle from \%hash use this.
423              
424             $thm = tied %{$href};
425              
426             In SCALAR context it returns the unblessed %hash pointer. In ARRAY context it returns
427             the unblessed %hash pointer and the package object/method pointer.
428              
429             =cut
430              
431             sub new {
432 8     8 1 2724 my($proto,@args) = @_;
433 8   50     66 my $class = ref $proto || $proto || __PACKAGE__;
434 8         12 my %x;
435 8         51 my $thm = tie %x, $class, @args;
436 8 50       47 return wantarray ? (\%x,$thm) : \%x;
437             }
438              
439             =item * $val = $thm->addkey('new_key' => 'existing_key');
440              
441             Add one or more keys to the shared key group for a particular value.
442              
443             input: array or array_ref,
444             existing_key
445             returns: hash value
446             or dies with stack trace
447              
448             Dies with stack trace if B does not exist OR if B key
449             belongs to another key set.
450              
451             Arguments may be a single SCALAR, ARRAY, or ARRAY_REF
452              
453             =cut
454              
455             sub addkey {
456 13     13 1 9474 my $self = shift;
457 13         30 $self->[5] = undef;
458 13         56 my($kh,$vh,$sh) = @{$self};
  13         33  
459 13         36 my($key,@new) = &_flip;
460 13 100       970 croak "key '$key' does not exist\n" unless exists $kh->{$key};
461 9         32 my $vi = $kh->{$key};
462 9         24 foreach(@new) {
463 14 50 33     59 if (exists $kh->{$_} && $kh->{$key} != $vi) {
464 0         0 my @kset = sort { $sh->{$vi}->{$a} <=> $sh->{$vi}->{$b} } keys %{$sh->{$vi}};
  0         0  
  0         0  
465 0         0 croak "key belongs to key set @kset\n";
466             }
467 14         43 $sh->{$vi}->{$_} = $self->[4]++;
468 14         47 $kh->{$_} = $vi;
469             }
470 9 100       44 $self->[7]->{ADDKEY}->($self,$key,$vi,\@new) if $self->[7];
471 9 50       40 $self->_rordvals() if $self->[4] > $indexmax;
472 9         50 return $vh->{$vi};
473             }
474              
475             =item * $val = ->remove('key');
476              
477             =item * $val = ->delkey('key'); alias for above
478              
479             Remove one or more keys from the shared key group for a particular value
480             If this operation removes the LAST key, then it performs a DELETE which is the same as:
481              
482             delete $hash{key};
483              
484             B returns a reverse list of the removed value's by key
485              
486             i.e. @val = remove(something);
487             or $val = remove(something);
488              
489             Arguments may be a single SCALAR, ARRAY or ARRAY_REF
490              
491             =cut
492              
493             # DELETE above does
494             # array of deleted keys, array of deleted value indices
495             # $self->[7]->{DELETE}->($self,\@dkeys,\@vix) if $self->[7];
496             #
497             # sub delete DELETE a key
498             *delkey = \&remove;
499             sub remove {
500 7     7 1 6200 my($self,@ks) = @_;
501 7         12 my($kh,$vh,$sh) = @{$self};
  7         17  
502 7         12 $self->[5] = undef;
503 7 100       24 my $ks = ref $ks[0] ? $ks[0] : \@ks; # extract reference is first element was an array ref of keys
504 7         9 my @keys = @{_wash($ks)};
  7         16  
505 7         10 my @vals;
506 7         11 foreach my $key (@keys) {
507 12 50       37 if (exists $kh->{$key}) {
508 12         58 my $vi = $kh->{$key};
509 12         22 delete $kh->{$key};
510 12         584 unshift @vals, $vh->{$vi};
511 12         23 delete $sh->{$vi}->{$key};
512 12 100       13 unless (keys %{$sh->{$vi}}) { # if last element in set
  12         35  
513 2         4 delete $sh->{$vi}; # delete set values and keys
514 2         4 delete $vh->{$vi};
515 2 50       12 $self->[7]->{DELETE}->($self,[$key],[$vi]) if $self->[7]; # delete last key extension
516             } else {
517 10 100       38 $self->[7]->{DELKEY}->($self,$key,$vi) if $self->[7]; # not last key
518             }
519             } else { # bogus key
520 0         0 unshift @vals, undef;
521             }
522             }
523 7 100       55 return wantarray ? @vals : $vals[0];
524 0         0 $ks = \&delkey; # never reached, suppress warning
525             }
526              
527             =item * @ordered_keys = $thm->keylist('foo');
528              
529             =item * @allkeys_by_order = $thm->keylist();
530              
531             Returns all the keys in the group that includes the KEY 'foo' in the order
532             that they were added to the %hash;
533              
534             If no argument is specified, returns all the keys in the %hash in the order
535             that they were added to the %hash
536              
537             input: key or EMPTY
538             returns: @ordered_keys
539              
540             returns: () if $key is not in the %hash
541              
542             =cut
543              
544             sub keylist {
545 4     4 1 9126 my($self,$key) = @_;
546 4         10 $self->[5] = undef;
547 4         5 my($kh,$vh,$sh) = @{$self};
  4         8  
548 4 100       14 if (defined $key) {
549 2 50       10 return () unless exists $kh->{$key};
550 2         6 my $vi = $kh->{$key};
551 2         3 return sort { $sh->{$vi}->{$a} <=> $sh->{$vi}->{$b} } keys %{$sh->{$vi}};
  18         34  
  2         11  
552             }
553 2         30 my %ak; # key => order
554 2         5 foreach(keys %{$sh}) {
  2         8  
555 16         15 my @keys = keys %{$sh->{$_}};
  16         30  
556 16         18 @ak{@keys} = @{$sh->{$_}}{@keys};
  16         45  
557             }
558 2         13 return sort { $ak{$a} <=> $ak{$b} } keys %ak;
  109         118  
559             }
560              
561             =item * @keys = $thm->slotlist($i);
562              
563             Returns one key from each key group in position B<$i>.
564              
565             i.e.
566             $thm = tie %hash, 'Tie::Hash::MultiKey';
567              
568             $hash{['a','b','c']} = 'one';
569             $hash{['d','e','f']} = 'two';
570             $hash{'g'} = 'three';
571             $hash{['h','i','j']} = 'four';
572              
573             @slotkeys = $thm->slotlist(1);
574              
575             will produce ('b','e', undef, 'i')
576              
577             All the keys at index '1' for the groups to which they were added, in the
578             order which the FIRST KEY in the group was added to the %hash. If there is no key in the
579             specified slot, an undef is returned for that position.
580              
581             =cut
582              
583             sub slotlist($$) {
584 10     10 1 2514 my($self,$i) = @_;
585 10         17 $self->[5] = undef;
586 10         11 my($kh,$vh,$sh) = @{$self};
  10         15  
587 10         14 my %kbs; # order => key
588 10         10 foreach(keys %{$sh}) {
  10         29  
589 80         93 my $slot = $sh->{$_};
590 80         68 my @keys = sort { $slot->{$a} <=> $slot->{$b} } keys %{$slot};
  125         203  
  80         175  
591 80         6244 my $key = $keys[$i];
592 80         700 $kbs{$slot->{pop @keys}} = $key; # undef is there is no key
593             }
594 10         36 my @order = sort { $a <=> $b } keys %kbs;
  153         158  
595 10         73 return @kbs{@order};
596             }
597              
598             =item * $thm->size;
599              
600             Returns the number of ITEMS in the hash (not the number of keys). Should be
601             faster than ... scalar @values
602              
603             =cut
604              
605             sub size {
606 4     4 1 3898 $_[0]->[5] = undef;
607 4         7 return scalar values %{$_[0]->[1]};
  4         94  
608             }
609              
610             =item * $thm->consolidate;
611              
612             USE WITH CAUTION
613              
614             Consolidate all keys with the same values into common groups.
615              
616             returns: number of consolidated key groups
617              
618             =cut
619              
620             # added 3 sorts to keep key order constant across multiple platforms for testing purposes
621             # while this is inefficient, this method should rarely be used by competent developers
622              
623             sub consolidate {
624 3     3 1 5091 my $self = shift;
625 3         7 $self->[5] = undef;
626 3         5 my($kh,$vh,$sh) = @{$self};
  3         8  
627             # $kbv value => [keys]
628             # $ko keys => order
629             # $ovm value => [old vi order]
630 3         7 my (%kbv,%ko,%ovm); # keys by value, key order, old vi order by value
631 3         45 foreach my $vi (sort keys %$vh) { # sort for cross platform testing ***
632 34         46 my $v = $vh->{$vi};
633             # while (my($vi,$v) = each %$vh) {
634             # consolidate key sets of shared keys
635 34 100       63 if (exists $ovm{$v}) {
636 15         14 push @{$ovm{$v}}, $vi;
  15         28  
637             } else {
638 19         46 $ovm{$v} = [$vi];
639             }
640 34         62 my @keys = sort keys %{$sh->{$vi}}; # sort for cross platform testing ***
  34         86  
641 34         39 @ko{@keys} = @{$sh->{$vi}}{@keys}; # preserve key order
  34         76  
642 34 100       59 if (exists $kbv{$v}) { # have key group?
643 15         14 push @{$kbv{$v}}, @keys; # add keys
  15         35  
644             } else {
645 19         55 $kbv{$v} = [@keys]; # start new key group
646             }
647             }
648 3         10 my $ko = $self->[4]; # save next key order number
649 3         9 _clear($self);
650 3         5 my %nvi2ovi;
651 3         46 foreach my $v (sort keys %kbv) { # sort for cross platform testing ***
652 19         20 my @k = @{$kbv{$v}};
  19         40  
653             # while (my($v,$k) = each %kbv) { # values by key
654 19         32 my $indx = $self->[3]++;
655 19         29 $nvi2ovi{$indx} = $ovm{$v}; # create new => [old] map
656 19         27 $vh->{$indx} = $v; # value
657 19         28 @{$sh->{$indx}}{@k} = @ko{@k}; # restore shared keys and order
  19         49  
658 19         32 map { $kh->{$_} = $indx } @k;
  49         99  
659             }
660 3         6 $self->[4] = $ko;
661 3 100       533 $self->[7]->{CONSOLD}->($self,\%kbv,\%ko,\%nvi2ovi) if $self->[7];
662 3 50       32 $self->_rordkeys() if $self->[3] > $indexmax;
663 3         24 $self->[3];
664             }
665              
666             =item @ordered_vals = $thm->vals();
667              
668             Return a list of values in the order they were added.
669              
670             =cut
671              
672             sub vals {
673 2     2 1 2436 $_[0]->[5] = undef;
674 2         5 map { $_[0]->[1]->{$_} } sort { $a <=> $b } keys %{$_[0]->[1]};
  10         28  
  14         21  
  2         39  
675             }
676              
677             =item * ($href,$thm) = $thm->clone();
678              
679             This method returns an UNBLESSED reference to an anonymous tied %hash that
680             is a deep copy of the parent object.
681              
682             input: none
683             returns: unblessed tied %hash reference,
684             object handle
685              
686             To get the object handle from \%hash use this.
687              
688             $thm = tied %{$href};
689              
690             In SCALAR context it returns the unblessed %hash pointer. In ARRAY context it returns
691             the unblessed %hash pointer and the package object/method pointer.
692              
693             i.e.
694             $newRef = $thm->clone();
695              
696             $newRref->{'a','b'} = 'content'
697              
698             $newThm = tied %{$newRef};
699              
700             =item * $new_thm = $thm->copy(tie %new,'Tie::Hash::MultiKey');
701              
702             This method deep copies a MultiKey %hash to another B %hash. It may
703             be invoked on an existing tied object handle or a reference to a tied %hash.
704              
705             input: object handle OR reference to tied %hash
706             returns: object handle / method pointer
707              
708             i.e
709             $thm = tie %hash,'Tie::Hash::MultiKey';
710             $newThm = $thm->copy(tie %new,'Tie::Hash::MultiKey');
711             or
712             tie %new,'Tie::Hash::MultiKey');
713             $newThm = $thm->copy(\%new);
714              
715             NOTE: this method duplicates the data stored in the parent %hash,
716             overwriting and destroying anything that may have been stored in the copy
717             target.
718              
719             =back
720              
721             =cut
722              
723             sub copy {
724 3     3 1 1718 my($self,$copy) = @_;
725 3 50       17 croak "no target specified\n"
726             unless defined $copy;
727 3 50 33     24 croak "argument is not a ", (ref $self) ," object\n"
      66        
728             unless ref $copy eq ref $self || (ref $copy eq 'HASH' && ref ($copy = tied %$copy) eq ref $self);
729 3 100       20 CLEAR($copy) unless $copy->[3] == 0; # skip if empty hash
730 3         14 _copy($self,$copy);
731             }
732              
733             sub clone {
734 2     2 1 575 my($href,$copy) = &new;
735 2         10 _copy($_[0],$copy);
736 2 50       23 return wantarray ? ($href,$copy) : $href;
737             }
738              
739             sub _copy {
740 5     5   10 my($self,$copy) = @_;
741 5         10 $self->[5] = undef;
742 5         10 my($kh,$vh,$sh) = @{$self};
  5         12  
743 5         40 my @keys = keys %$kh;
744 5         14 my @vals = @{$kh}{@keys};
  5         22  
745 5         10 my($ckh,$cvh,$csh) = @{$copy};
  5         12  
746 5         12 @{$ckh}{@keys} = @vals; # clone keys
  5         24  
747 5         24 @{$cvh}{@vals} = @{$vh}{@vals}; # clone value index
  5         22  
  5         10  
748 5         15 foreach (@vals) {
749 31         35 @keys = keys %{$sh->{$_}};
  31         143  
750 31         48 @{$csh->{$_}}{@keys} = @{$sh->{$_}}{@keys};
  31         96  
  31         59  
751             }
752 5         13 @{$copy}[3,4,5] = @{$self}[3,4,5];
  5         10  
  5         13  
753 5 100       21 if ($self->[7]) { # if extensions
754             # $copy->[7] = $self->[7]; # copy extension pointers
755 1         3 @vals = keys %{$vh};
  1         5  
756 1         6 $self->[7]->{COPY}->($self,$copy,\@vals);
757             }
758 5         96 $copy;
759             }
760              
761             # belt and suspenders routines in case the indices or order index get to big
762              
763             sub _rordkeys {
764 3     3   3183 my $self = shift;
765 3         8 my $nord = 0; # new order
766 3         8 my $sh = $self->[2];
767 3         7 my $osh = {}; # a hash of all old shared keys with their order
768 3         15 foreach (keys %$sh) {
769 7         10 my @keys = keys %{$sh->{$_}};
  7         25  
770 7         16 @{$osh}{@keys} = @{$sh->{$_}}{@keys};
  7         30  
  7         19  
771             }
772 3         37 my %rsh = reverse %$osh; # reverse array to reorder unique numeric order numbers
773 3         9 my $nsh = {}; # new shared order hash
774 3         19 %$nsh = map { ($rsh{$_}, $nord++) } sort { $a <=> $b } keys %rsh;
  23         59  
  45         58  
775 3         14 foreach (keys %$sh) {
776 7         19 my @keys = keys %{$sh->{$_}};
  7         24  
777 7         12 @{$sh->{$_}}{@keys} = @{$nsh}{@keys}; # replace old order with new order
  7         29  
  7         16  
778             }
779 3 100       19 $self->[7]->{REORDERK}->($self,$nsh) if $self->[7];
780 3         40 $self->[4] = $nord;
781             }
782              
783             sub _rordvals {
784 3     3   3578 my $self = shift;
785 3         8 my $ni = 0; # new index
786 3         6 my($kh,$vh,$sh) = @{$self};
  3         8  
787 3         8 my $nvh = {}; # new value hash
788 3         6 my $nsh = {}; # new shared key hash
789 3         4 my %kmap; # map for primary key hash and value hash
790 3         15 foreach (sort keys %$vh) { # vh and sh share common keys
791 7         21 $nvh->{$ni} = $vh->{$_};
792 7         15 $nsh->{$ni} = $sh->{$_};
793 7         55 $kmap{$_} = $ni++;
794             }
795 3         11 foreach(keys %$kh) {
796 23         101 $kh->{$_} = $kmap{$kh->{$_}}; # replace old index pointer with new index pointer
797             }
798 3         9 @{$self}[1,2,3] = ($nvh,$nsh,$ni);
  3         12  
799 3 100       32 $self->[7]->{REORDERV}->($self,\%kmap) if $self->[7]; # if extensions
800             }
801              
802 0     0     sub DESTROY {}
803              
804             1;
805              
806             __END__