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;
|