File Coverage

blib/lib/Acme/CPANModules/OrderedHash.pm
Criterion Covered Total %
statement 3 3 100.0
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 4 4 100.0


line stmt bran cond sub pod time code
1             package Acme::CPANModules::OrderedHash;
2              
3 1     1   506284 use strict;
  1         3  
  1         2956  
4              
5             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
6             our $DATE = '2025-04-15'; # DATE
7             our $DIST = 'Acme-CPANModules-OrderedHash'; # DIST
8             our $VERSION = '0.004'; # VERSION
9              
10             our $LIST = {
11             summary => "List of modules that provide ordered hash data type",
12             description => <<'MARKDOWN',
13              
14             When you ask a Perl's hash for the list of keys, the answer comes back
15             unordered. In fact, Perl explicitly randomizes the order of keys it returns
16             everytime. The random ordering is a (security) feature, not a bug. However,
17             sometimes you want to know the order of insertion. These modules provide you
18             with an ordered hash; most of them implement it by recording the order of
19             insertion of keys in an additional array.
20              
21             Other related modules:
22              
23             - will automatically sort keys when you call `keys()`,
24             `values()`, `each()`. But this module does not maintain insertion order.
25              
26             MARKDOWN
27             entries => [
28              
29             {
30             module => 'Tie::IxHash',
31             bench_code => sub {
32             my ($op, $numkeys, $numrep) = @_;
33              
34             tie my %hash, "Tie::IxHash";
35             for (1..$numkeys) { $hash{"key$_"} = $_ }
36              
37             if ($op eq 'delete') {
38             for (1..$numkeys) { delete $hash{"key$_"} }
39             } elsif ($op eq 'keys') {
40             for (1..$numrep) { my @keys = keys %hash }
41             } elsif ($op eq 'iterate') {
42             for (1..$numrep) { while (my ($k,$v) = each %hash) {} }
43             }
44             },
45             },
46              
47             {
48             module => 'Hash::Ordered',
49             bench_code => sub {
50             my ($op, $numkeys, $numrep) = @_;
51              
52             my $hash = Hash::Ordered->new;
53             for (1..$numkeys) { $hash->set("key$_" => $_) }
54              
55             if ($op eq 'delete') {
56             for (1..$numkeys) { $hash->delete("key$_") }
57             } elsif ($op eq 'keys') {
58             for (1..$numrep) { my @keys = $hash->keys }
59             } elsif ($op eq 'iterate') {
60             for (1..$numrep) { my $iter = $hash->iterator; while (my ($k,$v) = $iter->()) {} }
61             }
62             },
63             },
64              
65             {
66             module => 'Tie::Hash::Indexed',
67             description => <<'MARKDOWN',
68              
69             Provides two interfaces: tied hash and OO.
70              
71             MARKDOWN
72             bench_code => sub {
73             my ($op, $numkeys, $numrep) = @_;
74              
75             tie my %hash, "Tie::Hash::Indexed";
76             for (1..$numkeys) { $hash{"key$_"} = $_ }
77              
78             if ($op eq 'delete') {
79             for (1..$numkeys) { delete $hash{"key$_"} }
80             } elsif ($op eq 'keys') {
81             for (1..$numrep) { my @keys = keys %hash }
82             } elsif ($op eq 'iterate') {
83             for (1..$numrep) { while (my ($k,$v) = each %hash) {} }
84             }
85             },
86             },
87              
88             {
89             module => 'Tie::LLHash',
90             bench_code => sub {
91             my ($op, $numkeys, $numrep) = @_;
92              
93             tie my %hash, "Tie::LLHash";
94             for (1..$numkeys) { (tied %hash)->insert("key$_" => $_) }
95              
96             if ($op eq 'delete') {
97             for (1..$numkeys) { delete $hash{"key$_"} }
98             } elsif ($op eq 'keys') {
99             for (1..$numrep) { my @keys = keys %hash }
100             } elsif ($op eq 'iterate') {
101             for (1..$numrep) { while (my ($k,$v) = each %hash) {} }
102             }
103             },
104             },
105              
106             {
107             module => 'Tie::StoredOrderHash',
108             bench_code => sub {
109             my ($op, $numkeys, $numrep) = @_;
110              
111             tie my %hash, "Tie::StoredOrderHash";
112             for (1..$numkeys) { $hash{"key$_"} = $_ }
113              
114             if ($op eq 'delete') {
115             for (1..$numkeys) { delete $hash{"key$_"} }
116             } elsif ($op eq 'keys') {
117             for (1..$numrep) { my @keys = keys %hash }
118             } elsif ($op eq 'iterate') {
119             for (1..$numrep) { while (my ($k,$v) = each %hash) {} }
120             }
121             },
122             },
123              
124             {
125             module => 'Array::OrdHash',
126             description => <<'MARKDOWN',
127              
128             Provide something closest to PHP's associative array, where you can refer
129             elements by key or by numeric index, and insertion order is remembered.
130              
131             MARKDOWN
132             bench_code => sub {
133             my ($op, $numkeys, $numrep) = @_;
134              
135             my $hash = Array::OrdHash->new;
136             for (1..$numkeys) { $hash->{"key$_"} = $_ }
137              
138             if ($op eq 'delete') {
139             for (1..$numkeys) { delete $hash->{"key$_"} }
140             } elsif ($op eq 'keys') {
141             for (1..$numrep) { my @keys = keys %$hash }
142             } elsif ($op eq 'iterate') {
143             for (1..$numrep) { while (my ($k,$v) = each %$hash) {} }
144             }
145             },
146             },
147              
148             {
149             module => 'List::Unique::DeterministicOrder',
150             description => <<'MARKDOWN',
151              
152             Provide a list, not hash.
153              
154             MARKDOWN
155             bench_tags => ["no_iterate"].
156             bench_code => sub {
157             my ($op, $numkeys, $numrep) = @_;
158              
159             my $hash = List::Unique::DeterministicOrder->new(data=>[]);
160             for (1..$numkeys) { $hash->push("key$_") }
161              
162             if ($op eq 'delete') {
163             for (1..$numkeys) { $hash->delete("key$_") }
164             } elsif ($op eq 'keys') {
165             for (1..$numrep) { my @keys = $hash->keys }
166             } elsif ($op eq 'iterate') {
167             for (1..$numrep) { while (my ($k,$v) = each %$hash) {} }
168             }
169             },
170             },
171              
172             {
173             module => 'Tree::RB::XS',
174             description => <<'MARKDOWN',
175              
176             Multi-purpose tree data structure which can record insertion order and act as an
177             ordered hash. Use `track_recent => 1, keys_in_recent_order => 1` options. Can
178             be used as a tied hash, or as an object (faster).
179              
180             MARKDOWN
181             bench_code => sub {
182             my ($op, $numkeys, $numrep) = @_;
183              
184             my $tree= Tree::RB::XS->new(compare_fn => 'str', track_recent => 1, keys_in_recent_order => 1);
185             for (1..$numkeys) { $tree->insert("key$_") }
186              
187             if ($op eq 'delete') {
188             for (1..$numkeys) { $tree->delete("key$_") }
189             } elsif ($op eq 'keys') {
190             for (1..$numrep) { my @keys= $tree->keys }
191             } elsif ($op eq 'iterate') {
192             for (1..$numrep) { my $iter = $tree->iter; while (my $v = $iter->next) {} }
193             }
194             },
195             },
196             ],
197              
198             bench_datasets => [
199             {name=>'insert 1000 pairs', argv => ['insert', 1000]},
200             {name=>'insert 1000 pairs + delete', argv => ['delete', 1000]},
201             {name=>'insert 1000 pairs + return keys 100 times', argv => ['keys', 1000, 100]},
202             {name=>'insert 1000 pairs + iterate 10 times', argv => ['iterate', 1000, 10], exclude_participant_tags => ['no_iterate']},
203             ],
204             };
205              
206             1;
207             # ABSTRACT: List of modules that provide ordered hash data type
208              
209             __END__