File Coverage

blib/lib/Tie/MultiKeyInsertOrderHash.pm
Criterion Covered Total %
statement 51 57 89.4
branch 10 14 71.4
condition 2 3 66.6
subroutine 12 14 85.7
pod n/a
total 75 88 85.2


line stmt bran cond sub pod time code
1             #
2             # MultiKeyInsertOrderHash.pm - save multiple keys in insertion order
3             #
4             # 2008 - Marc-Sebastian Lucksch
5             # perl@maluku.de
6             #
7             # Partly based on Tie::InsertOrderHash from
8             # B. K. Oxley (binkley) binkley@bigfoot.comE
9             #
10            
11             =head1 NAME
12            
13             Tie::MultiKeyInsertOrderHash
14            
15             =head2 DESCRIPTION
16            
17             Store multiple keys in a hash in insertion order
18            
19             =head1 SYNOPSIS
20            
21             tie my %hash => 'Tie::MultiKeyInsertOrderHash';
22             $hash{Say}="Hello World";
23             $hash{Do}="wave";
24             $hash{Say}="Good-Bye";
25             $hash{Do}="leave";
26            
27             while (my ($key, $value) = each (%hash)) {
28             print "Action: $key Option: $value\n";
29             }
30             print "I said: '", join ("' and '",@{$hash{Say}}),"'\n";
31             print "I did: '", join ("' and '",@{$hash{Do}}),"'\n";
32            
33             print "The first thing I said was $hash{Say}->[0]\n";
34             print "The last thing I said was $hash{Say}->[-1]\n";
35            
36            
37             Or:
38            
39             tie my %hash => 'Tie::MultiKeyInsertOrderHash',A,1,B,2,A,3,B,4; #Initial values.
40            
41             =head1 Notes
42            
43             To complete overwrite a value use:
44            
45             delete $hash{Value}
46             $hash{Value}="newvalue";
47            
48             $hash{Value} will return an array of all values of that key; This won't work as you expect:
49            
50             foreach my $key (keys %hash) {
51             print $key; #This will work and print the key in the right order, but the key will be printed multiple times.
52             print $hash{$key}; #This will print "ARRAY(......)";
53             print $hash{$key}->[0]; # This will print the first value multiple times.
54             print join(@{$hash{$key}}); #This will print all values of that key multiple times.
55             }
56            
57             Better use:
58             while (my ($key, $value) = each (%hash)) {
59             print $key;
60             print $value; #This will print every value only once and in the right order.
61             }
62            
63             OR maybe:
64            
65             my %seen;
66             foreach my $key (grep !$seen{$_}++,keys %hash) {
67             print $key;
68             print join(@{$hash{$key}}); #This will print the keys in the right order, but the values grouped by keys.
69             }
70            
71             =cut
72            
73             package Tie::MultiKeyInsertOrderHash;
74            
75             require 5.006_001;
76 1     1   66177 use strict;
  1         2  
  1         44  
77 1     1   6 use warnings;
  1         1  
  1         52  
78            
79             our $VERSION = 0.1;
80            
81 1     1   6 use base qw(Tie::Hash);
  1         6  
  1         1057  
82            
83 1     1   2662 use Data::Dumper;
  1         26434  
  1         96  
84            
85 1     1   44 use Carp qw/cluck/;
  1         3  
  1         815  
86            
87             sub TIEHASH {
88 1     1   18 my $class = shift;
89 0         0 bless [
90 1         13 [@_[grep { $_ % 2 == 0 } (0..$#_)]],
91             {@_},
92             0,
93             {},
94             undef
95             ],$class;
96             }
97            
98             sub STORE {
99 6     6   2294 push @{$_[0]->[0]}, $_[1];
  6         25  
100 6         15 $_[0]->[2] = -1;
101 6         9 push @{$_[0]->[1]->{$_[1]}},$_[2];
  6         32  
102             }
103            
104             sub FETCH {
105             #cluck();
106 15 100 66 15   1583 if ($_[0]->[4] and $_[0]->[4]->[0] eq $_[1]) {
107 5         12 my $r=$_[0]->[4]->[1];
108 5         13 $_[0]->[4]=undef;
109 5         51 return $r;
110             }
111 10         60 return $_[0]->[1]->{$_[1]};
112             }
113            
114             sub FIRSTKEY {
115             #print STDERR Data::Dumper->Dump([@_]);
116 3     3   24 $_[0]->[3]={};
117 3         12 $_[0]->[2] = 0;
118 3 50       74 return $_[0]->[4]=undef unless exists $_[0]->[0]->[$_[0]->[2]];
119 3         9 my $key = $_[0]->[0]->[0];
120 3 50       16 $_[0]->[3]->{$key}=1 unless $_[0]->[3]->{$key};
121 3         17 $_[0]->[4]=[$key, $_[0]->[1]->{$key}->[0]];
122 3         24 return $key
123             }
124            
125            
126             sub NEXTKEY {
127 14     14   32 my $i = $_[0]->[2];
128 14 50       43 return $_[0]->[4]=undef unless exists $_[0]->[0]->[$i];
129 14 50       43 if ($_[0]->[0]->[$i] eq $_[1]) {
130 14         27 $i = ++$_[0]->[2] ;
131 14 100       56 return $_[0]->[4]=undef unless exists $_[0]->[0]->[$i];
132             }
133 12         16 my $key = ${$_[0]->[0]}[$i];
  12         537  
134 12 100       65 $_[0]->[3]->{$key}=0 unless $_[0]->[3]->{$key};
135 12         24 $_[0]->[3]->{$key}++;
136             #print STDERR "\nKey=$_[0]->[3]->{$key}\n$_[0]->[1]->{$key}->[$_[0]->[3]->{$key}-1]\n\n";
137 12         57 $_[0]->[4]=[$key, $_[0]->[1]->{$key}->[$_[0]->[3]->{$key}-1]];
138 12         1227 return $key;
139             }
140            
141             sub EXISTS {
142 2     2   22 return exists $_[0]->[1]->{$_[1]}
143             }
144            
145             sub DELETE {
146 1     1   4 @{$_[0]->[0]} = grep { $_ ne $_[1] } @{$_[0]->[0]};
  1         4  
  1         5  
  1         6  
147 1         6 delete $_[0]->[1]->{$_[1]};
148             }
149            
150             sub CLEAR {
151 0     0     $_[0]->[0] = [];
152 0           $_[0]->[1] = {};
153 0           $_[0]->[2] = 0;
154 0           $_[0]->[3] = {};
155             }
156            
157             sub SCALAR {
158 0     0     return scalar $_[0]->[0];
159             }
160            
161             =head1 BUGS
162            
163             values() and scalar each() won't work do what you expect at all, because they call values(%hash) calls $hash{key} for each key, so it will return and array of arrayrefs
164             scalar each() works, but there is no way to find out in which context each was called, so it will screw up the next $hash{key} request.
165            
166             Better only use ONLY this for iterating over this hash
167            
168             while (my ($key, $value) = each (%hash)) {
169             #do something with $key and $value
170             }
171            
172             =head1 AUTHOR
173            
174             Marc-Sebastian Lucksch
175            
176             perl@maluku.de
177            
178             =cut
179            
180             #It seems to me that wantarray is never set in FIRSTKEY or NEXTKEY even if each is called in list context. It will always trigger FETCH.
181             1;