1 | use 5.006;
|
---|
2 | use strict;
|
---|
3 | use warnings;
|
---|
4 |
|
---|
5 | package Hash::Ordered;
|
---|
6 | # ABSTRACT: A compact, pure-Perl ordered hash class
|
---|
7 | our $VERSION = '0.002'; # VERSION
|
---|
8 |
|
---|
9 | use Carp ();
|
---|
10 | use List::Util 1.09 ();
|
---|
11 |
|
---|
12 | use constant {
|
---|
13 | _DATA => 0,
|
---|
14 | _KEYS => 1,
|
---|
15 | };
|
---|
16 |
|
---|
17 | use overload 'bool' => sub { scalar @{ $_[0]->[_KEYS] } }, fallback => 1;
|
---|
18 |
|
---|
19 | #pod =method new
|
---|
20 | #pod
|
---|
21 | #pod $oh = Hash::Ordered->new;
|
---|
22 | #pod $oh = Hash::Ordered->new( @pairs );
|
---|
23 | #pod
|
---|
24 | #pod Constructs an object, with an optional list of key-value pairs.
|
---|
25 | #pod
|
---|
26 | #pod =cut
|
---|
27 |
|
---|
28 | sub new {
|
---|
29 | my ( $class, @pairs ) = @_;
|
---|
30 |
|
---|
31 | return bless [ {}, [] ], $class unless @pairs;
|
---|
32 |
|
---|
33 | Carp::croak("new() requires key-value pairs") unless @pairs % 2 == 0;
|
---|
34 |
|
---|
35 | my $self = [ {@pairs}, [ map { $_ % 2 == 0 ? ( $pairs[$_] ) : () } 0 .. $#pairs ] ];
|
---|
36 |
|
---|
37 | return bless $self, $class;
|
---|
38 | }
|
---|
39 |
|
---|
40 | #pod =method clone
|
---|
41 | #pod
|
---|
42 | #pod $oh2 = $oh->clone;
|
---|
43 | #pod $oh2 = $oh->clone( @keys );
|
---|
44 | #pod
|
---|
45 | #pod Creates a shallow copy of an ordered hash object. If no arguments are
|
---|
46 | #pod given, it produces an exact copy. If a list of keys is given, the new
|
---|
47 | #pod object includes only those keys in the given order. Keys that aren't
|
---|
48 | #pod in the original will have the value C<undef>.
|
---|
49 | #pod
|
---|
50 | #pod =cut
|
---|
51 |
|
---|
52 | sub clone {
|
---|
53 | my ( $self, @keys ) = @_;
|
---|
54 | my $clone;
|
---|
55 | if (@keys) {
|
---|
56 | my %subhash;
|
---|
57 | @subhash{@keys} = @{ $self->[_DATA] }{@keys};
|
---|
58 | $clone = [ \%subhash, \@keys ];
|
---|
59 | }
|
---|
60 | else {
|
---|
61 | $clone = [ { %{ $self->[_DATA] } }, [ @{ $self->[_KEYS] } ] ];
|
---|
62 | }
|
---|
63 | return bless $clone, ref $self;
|
---|
64 | }
|
---|
65 |
|
---|
66 | #pod =method keys
|
---|
67 | #pod
|
---|
68 | #pod @keys = $oh->keys;
|
---|
69 | #pod
|
---|
70 | #pod Returns the ordered list of keys.
|
---|
71 | #pod
|
---|
72 | #pod =cut
|
---|
73 |
|
---|
74 | sub keys {
|
---|
75 | my ($self) = @_;
|
---|
76 | return @{ $self->[_KEYS] };
|
---|
77 | }
|
---|
78 |
|
---|
79 | #pod =method values
|
---|
80 | #pod
|
---|
81 | #pod @values = $oh->values;
|
---|
82 | #pod @values = $oh->values( @keys );
|
---|
83 | #pod
|
---|
84 | #pod Returns an ordered list of values. If no arguments are given, returns
|
---|
85 | #pod the ordered values of the entire hash. If a list of keys is given, returns
|
---|
86 | #pod values in order corresponding to those keys. If a key does not exist, C<undef>
|
---|
87 | #pod will be returned for that value.
|
---|
88 | #pod
|
---|
89 | #pod =cut
|
---|
90 |
|
---|
91 | sub values {
|
---|
92 | my ( $self, @keys ) = @_;
|
---|
93 | return map { $self->[_DATA]{$_} } ( @keys ? @keys : @{ $self->[_KEYS] } );
|
---|
94 | }
|
---|
95 |
|
---|
96 | #pod =method get
|
---|
97 | #pod
|
---|
98 | #pod $value = $oh->get("some key");
|
---|
99 | #pod
|
---|
100 | #pod Returns the value associated with the key, or C<undef> if it does not exist in
|
---|
101 | #pod the hash.
|
---|
102 | #pod
|
---|
103 | #pod =cut
|
---|
104 |
|
---|
105 | sub get {
|
---|
106 | my ( $self, $key ) = @_;
|
---|
107 | return $self->[_DATA]{$key};
|
---|
108 | }
|
---|
109 |
|
---|
110 | #pod =method set
|
---|
111 | #pod
|
---|
112 | #pod $oh->set("some key" => "some value");
|
---|
113 | #pod
|
---|
114 | #pod Associates a value with a key and returns the value. If the key does not
|
---|
115 | #pod already exist in the hash, it will be added at the end.
|
---|
116 | #pod
|
---|
117 | #pod =cut
|
---|
118 |
|
---|
119 | sub set {
|
---|
120 | my ( $self, $key, $value ) = @_;
|
---|
121 | if ( !exists $self->[_DATA]{$key} ) {
|
---|
122 | push @{ $self->[_KEYS] }, $key;
|
---|
123 | }
|
---|
124 | return $self->[_DATA]{$key} = $value;
|
---|
125 | }
|
---|
126 |
|
---|
127 | #pod =method exists
|
---|
128 | #pod
|
---|
129 | #pod if ( $oh->exists("some key") ) { ... }
|
---|
130 | #pod
|
---|
131 | #pod Test if some key exists in the hash (without creating it).
|
---|
132 | #pod
|
---|
133 | #pod =cut
|
---|
134 |
|
---|
135 | sub exists {
|
---|
136 | my ( $self, $key ) = @_;
|
---|
137 | return exists $self->[_DATA]{$key};
|
---|
138 | }
|
---|
139 |
|
---|
140 | #pod =method delete
|
---|
141 | #pod
|
---|
142 | #pod $value = $oh->delete("some key");
|
---|
143 | #pod
|
---|
144 | #pod Removes a key-value pair from the hash and returns the value. This
|
---|
145 | #pod is expensive, as the ordered list of keys has to be updated.
|
---|
146 | #pod
|
---|
147 | #pod =cut
|
---|
148 |
|
---|
149 | sub delete {
|
---|
150 | my ( $self, $key ) = @_;
|
---|
151 | if ( exists $self->[_DATA]{$key} ) {
|
---|
152 | # XXX could put an index on this later if linear search is too slow
|
---|
153 | my $r = $self->[_KEYS];
|
---|
154 | my $i = List::Util::first { $r->[$_] eq $key } 0 .. $#$r;
|
---|
155 | splice @$r, $i, 1;
|
---|
156 | return delete $self->[_DATA]{$key};
|
---|
157 | }
|
---|
158 | return undef; ## no critic
|
---|
159 | }
|
---|
160 |
|
---|
161 | #pod =method push
|
---|
162 | #pod
|
---|
163 | #pod $oh->push( one => 1, two => 2);
|
---|
164 | #pod
|
---|
165 | #pod Add a list of key-value pairs to the end of the ordered hash. If a key already
|
---|
166 | #pod exists in the hash, it will be deleted and re-inserted at the end with the new
|
---|
167 | #pod value.
|
---|
168 | #pod
|
---|
169 | #pod Returns the number of keys after the push is complete.
|
---|
170 | #pod
|
---|
171 | #pod =cut
|
---|
172 |
|
---|
173 | sub push {
|
---|
174 | my ( $self, @pairs ) = @_;
|
---|
175 | while (@pairs) {
|
---|
176 | my ( $k, $v ) = splice( @pairs, 0, 2 );
|
---|
177 | if ( exists $self->[_DATA]{$k} ) {
|
---|
178 | # splice out key
|
---|
179 | # XXX could put an index on this later if linear search is too slow
|
---|
180 | my $r = $self->[_KEYS];
|
---|
181 | my $i = List::Util::first { $r->[$_] eq $k } 0 .. $#$r;
|
---|
182 | splice @$r, $i, 1;
|
---|
183 | }
|
---|
184 | push @{ $self->[_KEYS] }, $k;
|
---|
185 | $self->[_DATA]{$k} = $v;
|
---|
186 | }
|
---|
187 | return scalar @{ $self->[_KEYS] };
|
---|
188 | }
|
---|
189 |
|
---|
190 | #pod =method pop
|
---|
191 | #pod
|
---|
192 | #pod ($key, $value) = $oh->pop;
|
---|
193 | #pod
|
---|
194 | #pod Removes and returns the last key-value pair in the ordered hash.
|
---|
195 | #pod
|
---|
196 | #pod =cut
|
---|
197 |
|
---|
198 | sub pop {
|
---|
199 | my ($self) = @_;
|
---|
200 | my $key = pop @{ $self->[_KEYS] };
|
---|
201 | return $key, delete $self->[_DATA]{$key};
|
---|
202 | }
|
---|
203 |
|
---|
204 | #pod =method unshift
|
---|
205 | #pod
|
---|
206 | #pod $oh->unshift( one => 1, two => 2 );
|
---|
207 | #pod
|
---|
208 | #pod Adds a list of key-value pairs to the beginning of the ordered hash. If a key
|
---|
209 | #pod already exists, it will be deleted and re-inserted at the beginning with the
|
---|
210 | #pod new value.
|
---|
211 | #pod
|
---|
212 | #pod Returns the number of keys after the unshift is complete.
|
---|
213 | #pod
|
---|
214 | #pod =cut
|
---|
215 |
|
---|
216 | sub unshift {
|
---|
217 | my ( $self, @pairs ) = @_;
|
---|
218 | while (@pairs) {
|
---|
219 | my ( $k, $v ) = splice( @pairs, -2, 2 );
|
---|
220 | if ( exists $self->[_DATA]{$k} ) {
|
---|
221 | # splice out key
|
---|
222 | # XXX could put an index on this later if linear search is too slow
|
---|
223 | my $r = $self->[_KEYS];
|
---|
224 | my $i = List::Util::first { $r->[$_] eq $k } 0 .. $#$r;
|
---|
225 | splice @$r, $i, 1;
|
---|
226 | }
|
---|
227 | unshift @{ $self->[_KEYS] }, $k;
|
---|
228 | $self->[_DATA]{$k} = $v;
|
---|
229 | }
|
---|
230 | return scalar @{ $self->[_KEYS] };
|
---|
231 | }
|
---|
232 |
|
---|
233 | #pod =method shift
|
---|
234 | #pod
|
---|
235 | #pod ($key, $value) = $oh->shift;
|
---|
236 | #pod
|
---|
237 | #pod Removes and returns the first key-value pair in the ordered hash.
|
---|
238 | #pod
|
---|
239 | #pod =cut
|
---|
240 |
|
---|
241 | sub shift {
|
---|
242 | my ($self) = @_;
|
---|
243 | my $key = shift @{ $self->[_KEYS] };
|
---|
244 | return $key, delete $self->[_DATA]{$key};
|
---|
245 | }
|
---|
246 |
|
---|
247 | #pod =method merge
|
---|
248 | #pod
|
---|
249 | #pod $oh->merge( one => 1, two => 2 );
|
---|
250 | #pod
|
---|
251 | #pod Merges a list of key-value pairs into the ordered hash. If a key already
|
---|
252 | #pod exists, its value is replaced. Otherwise, the key-value pair is added at
|
---|
253 | #pod the end of the hash.
|
---|
254 | #pod
|
---|
255 | #pod =cut
|
---|
256 |
|
---|
257 | sub merge {
|
---|
258 | my ( $self, @pairs ) = @_;
|
---|
259 | while (@pairs) {
|
---|
260 | my ( $k, $v ) = splice( @pairs, -2, 2 );
|
---|
261 | if ( !exists $self->[_DATA]{$k} ) {
|
---|
262 | CORE::push @{ $self->[_KEYS] }, $k;
|
---|
263 | }
|
---|
264 | $self->[_DATA]{$k} = $v;
|
---|
265 | }
|
---|
266 | return scalar @{ $self->[_KEYS] };
|
---|
267 | }
|
---|
268 |
|
---|
269 | #pod =method as_list
|
---|
270 | #pod
|
---|
271 | #pod @pairs = $oh->as_list;
|
---|
272 | #pod @pairs = $oh->as_list( @keys );
|
---|
273 | #pod
|
---|
274 | #pod Returns an ordered list of key-value pairs. If no arguments are given, all
|
---|
275 | #pod pairs in the hash are returned. If a list of keys is given, the returned list
|
---|
276 | #pod includes only those key-value pairs in the given order. Keys that aren't in
|
---|
277 | #pod the original will have the value C<undef>.
|
---|
278 | #pod
|
---|
279 | #pod =cut
|
---|
280 |
|
---|
281 | sub as_list {
|
---|
282 | my ( $self, @keys ) = @_;
|
---|
283 | @keys = @{ $self->[_KEYS] } unless @keys;
|
---|
284 | return map { ; $_ => $self->[_DATA]{$_} } @keys;
|
---|
285 | }
|
---|
286 |
|
---|
287 | #pod =method iterator
|
---|
288 | #pod
|
---|
289 | #pod $iter = $oh->iterator;
|
---|
290 | #pod $iter = $oh->iterator( reverse $oh->keys ); # reverse
|
---|
291 | #pod
|
---|
292 | #pod while ( my ($key,$value) = $iter->() ) { ... }
|
---|
293 | #pod
|
---|
294 | #pod Returns a code reference that returns a single key-value pair (in order) on
|
---|
295 | #pod each invocation, or the empty list if all keys are visited.
|
---|
296 | #pod
|
---|
297 | #pod If no arguments are given, the iterator walks the entire hash in order. If a
|
---|
298 | #pod list of keys is provided, the iterator walks the hash in that order. Unknown
|
---|
299 | #pod keys will return C<undef>.
|
---|
300 | #pod
|
---|
301 | #pod The list of keys to return is set when the iterator is generator. Keys added
|
---|
302 | #pod later will not be returned. Delete keys will return C<undef>.
|
---|
303 | #pod
|
---|
304 | #pod =cut
|
---|
305 |
|
---|
306 | sub iterator {
|
---|
307 | my ( $self, @keys ) = @_;
|
---|
308 | @keys = @{ $self->[_KEYS] } unless @keys;
|
---|
309 | my $data = $self->[_DATA];
|
---|
310 | return sub {
|
---|
311 | return unless @keys;
|
---|
312 | my $key = CORE::shift(@keys);
|
---|
313 | return ( $key => $data->{$key} );
|
---|
314 | };
|
---|
315 | }
|
---|
316 |
|
---|
317 | 1;
|
---|
318 |
|
---|
319 |
|
---|
320 | # vim: ts=4 sts=4 sw=4 et:
|
---|
321 |
|
---|
322 | __END__
|
---|
323 |
|
---|
324 | =pod
|
---|
325 |
|
---|
326 | =encoding UTF-8
|
---|
327 |
|
---|
328 | =head1 NAME
|
---|
329 |
|
---|
330 | Hash::Ordered - A compact, pure-Perl ordered hash class
|
---|
331 |
|
---|
332 | =head1 VERSION
|
---|
333 |
|
---|
334 | version 0.002
|
---|
335 |
|
---|
336 | =head1 SYNOPSIS
|
---|
337 |
|
---|
338 | use Hash::Ordered;
|
---|
339 |
|
---|
340 | my $oh = Hash::Ordered->new( a => 1 );
|
---|
341 |
|
---|
342 | $oh->get( 'a' );
|
---|
343 | $oh->set( 'a' => 2 );
|
---|
344 |
|
---|
345 | $oh->exists( 'a' );
|
---|
346 | $val = $oh->delete( 'a' );
|
---|
347 |
|
---|
348 | @keys = $oh->keys;
|
---|
349 | @vals = $oh->values;
|
---|
350 | @pairs = $oh->as_list
|
---|
351 |
|
---|
352 | $oh->push( c => 3, d => 4 );
|
---|
353 | $oh->unshift( e => 5, f => 6 );
|
---|
354 |
|
---|
355 | ( $k, $v ) = $oh->pop;
|
---|
356 | ( $k, $v ) = $oh->shift;
|
---|
357 |
|
---|
358 | $iter = $oh->iterator;
|
---|
359 | while( ( $k, $v ) = $iter->() ) { ... }
|
---|
360 |
|
---|
361 | $copy = $oh->clone;
|
---|
362 | $subset = $oh->clone( qw/c d/ );
|
---|
363 | $reversed = $oh->clone( reverse $oh->keys );
|
---|
364 |
|
---|
365 | @value_slice = $oh->values( qw/c f/ ); # qw/3 6/
|
---|
366 | @pairs_slice = $oh->as_list( qw/f e/ ); # qw/f 6 e 5/
|
---|
367 |
|
---|
368 | =head1 DESCRIPTION
|
---|
369 |
|
---|
370 | This module implements an ordered hash, meaning that it associates keys with
|
---|
371 | values like a Perl hash, but keeps the keys in a consistent order. Because it
|
---|
372 | is implemented as an object and manipulated with method calls, it is much
|
---|
373 | slower than a Perl hash. This is the cost of keeping order.
|
---|
374 |
|
---|
375 | =head1 METHODS
|
---|
376 |
|
---|
377 | =head2 new
|
---|
378 |
|
---|
379 | $oh = Hash::Ordered->new;
|
---|
380 | $oh = Hash::Ordered->new( @pairs );
|
---|
381 |
|
---|
382 | Constructs an object, with an optional list of key-value pairs.
|
---|
383 |
|
---|
384 | =head2 clone
|
---|
385 |
|
---|
386 | $oh2 = $oh->clone;
|
---|
387 | $oh2 = $oh->clone( @keys );
|
---|
388 |
|
---|
389 | Creates a shallow copy of an ordered hash object. If no arguments are
|
---|
390 | given, it produces an exact copy. If a list of keys is given, the new
|
---|
391 | object includes only those keys in the given order. Keys that aren't
|
---|
392 | in the original will have the value C<undef>.
|
---|
393 |
|
---|
394 | =head2 keys
|
---|
395 |
|
---|
396 | @keys = $oh->keys;
|
---|
397 |
|
---|
398 | Returns the ordered list of keys.
|
---|
399 |
|
---|
400 | =head2 values
|
---|
401 |
|
---|
402 | @values = $oh->values;
|
---|
403 | @values = $oh->values( @keys );
|
---|
404 |
|
---|
405 | Returns an ordered list of values. If no arguments are given, returns
|
---|
406 | the ordered values of the entire hash. If a list of keys is given, returns
|
---|
407 | values in order corresponding to those keys. If a key does not exist, C<undef>
|
---|
408 | will be returned for that value.
|
---|
409 |
|
---|
410 | =head2 get
|
---|
411 |
|
---|
412 | $value = $oh->get("some key");
|
---|
413 |
|
---|
414 | Returns the value associated with the key, or C<undef> if it does not exist in
|
---|
415 | the hash.
|
---|
416 |
|
---|
417 | =head2 set
|
---|
418 |
|
---|
419 | $oh->set("some key" => "some value");
|
---|
420 |
|
---|
421 | Associates a value with a key and returns the value. If the key does not
|
---|
422 | already exist in the hash, it will be added at the end.
|
---|
423 |
|
---|
424 | =head2 exists
|
---|
425 |
|
---|
426 | if ( $oh->exists("some key") ) { ... }
|
---|
427 |
|
---|
428 | Test if some key exists in the hash (without creating it).
|
---|
429 |
|
---|
430 | =head2 delete
|
---|
431 |
|
---|
432 | $value = $oh->delete("some key");
|
---|
433 |
|
---|
434 | Removes a key-value pair from the hash and returns the value. This
|
---|
435 | is expensive, as the ordered list of keys has to be updated.
|
---|
436 |
|
---|
437 | =head2 push
|
---|
438 |
|
---|
439 | $oh->push( one => 1, two => 2);
|
---|
440 |
|
---|
441 | Add a list of key-value pairs to the end of the ordered hash. If a key already
|
---|
442 | exists in the hash, it will be deleted and re-inserted at the end with the new
|
---|
443 | value.
|
---|
444 |
|
---|
445 | Returns the number of keys after the push is complete.
|
---|
446 |
|
---|
447 | =head2 pop
|
---|
448 |
|
---|
449 | ($key, $value) = $oh->pop;
|
---|
450 |
|
---|
451 | Removes and returns the last key-value pair in the ordered hash.
|
---|
452 |
|
---|
453 | =head2 unshift
|
---|
454 |
|
---|
455 | $oh->unshift( one => 1, two => 2 );
|
---|
456 |
|
---|
457 | Adds a list of key-value pairs to the beginning of the ordered hash. If a key
|
---|
458 | already exists, it will be deleted and re-inserted at the beginning with the
|
---|
459 | new value.
|
---|
460 |
|
---|
461 | Returns the number of keys after the unshift is complete.
|
---|
462 |
|
---|
463 | =head2 shift
|
---|
464 |
|
---|
465 | ($key, $value) = $oh->shift;
|
---|
466 |
|
---|
467 | Removes and returns the first key-value pair in the ordered hash.
|
---|
468 |
|
---|
469 | =head2 merge
|
---|
470 |
|
---|
471 | $oh->merge( one => 1, two => 2 );
|
---|
472 |
|
---|
473 | Merges a list of key-value pairs into the ordered hash. If a key already
|
---|
474 | exists, its value is replaced. Otherwise, the key-value pair is added at
|
---|
475 | the end of the hash.
|
---|
476 |
|
---|
477 | =head2 as_list
|
---|
478 |
|
---|
479 | @pairs = $oh->as_list;
|
---|
480 | @pairs = $oh->as_list( @keys );
|
---|
481 |
|
---|
482 | Returns an ordered list of key-value pairs. If no arguments are given, all
|
---|
483 | pairs in the hash are returned. If a list of keys is given, the returned list
|
---|
484 | includes only those key-value pairs in the given order. Keys that aren't in
|
---|
485 | the original will have the value C<undef>.
|
---|
486 |
|
---|
487 | =head2 iterator
|
---|
488 |
|
---|
489 | $iter = $oh->iterator;
|
---|
490 | $iter = $oh->iterator( reverse $oh->keys ); # reverse
|
---|
491 |
|
---|
492 | while ( my ($key,$value) = $iter->() ) { ... }
|
---|
493 |
|
---|
494 | Returns a code reference that returns a single key-value pair (in order) on
|
---|
495 | each invocation, or the empty list if all keys are visited.
|
---|
496 |
|
---|
497 | If no arguments are given, the iterator walks the entire hash in order. If a
|
---|
498 | list of keys is provided, the iterator walks the hash in that order. Unknown
|
---|
499 | keys will return C<undef>.
|
---|
500 |
|
---|
501 | The list of keys to return is set when the iterator is generator. Keys added
|
---|
502 | later will not be returned. Delete keys will return C<undef>.
|
---|
503 |
|
---|
504 | =head1 OVERLOADING
|
---|
505 |
|
---|
506 | =head2 Boolean
|
---|
507 |
|
---|
508 | if ( $oh ) { ... }
|
---|
509 |
|
---|
510 | When used in boolean context, a Hash::Ordered object is true if it has any entries
|
---|
511 | and false otherwise.
|
---|
512 |
|
---|
513 | =head1 MOTIVATION
|
---|
514 |
|
---|
515 | For a long time, I used L<Tie::IxHash> for ordered hashes, but I grew
|
---|
516 | frustrated with things it lacked, like a cheap way to copy an IxHash object or
|
---|
517 | a convenient iterator when not using the tied interface. As I looked at its
|
---|
518 | implementation, it seemed more complex than I though it needed, with an extra
|
---|
519 | level of indirection that slows data access.
|
---|
520 |
|
---|
521 | Given that frustration, I started experimenting with the simplest thing I
|
---|
522 | thought could work for an ordered hash: a hash of key-value pairs and an array
|
---|
523 | with key order.
|
---|
524 |
|
---|
525 | As I worked on this, I also started searching for other modules doing similar
|
---|
526 | things. What I found fell broadly into two camps: modules based on tie (even
|
---|
527 | if they offered an OO interface), and pure OO modules. They all either lacked
|
---|
528 | features I deemed necessary or else seemed overly-complex in either
|
---|
529 | implementation or API.
|
---|
530 |
|
---|
531 | Hash::Ordered attempts to find the sweet spot with simple implementation,
|
---|
532 | reasonably good efficiency for most common operations, and a rich, intuitive
|
---|
533 | API.
|
---|
534 |
|
---|
535 | =head1 SEE ALSO
|
---|
536 |
|
---|
537 | This section describes other ordered-hash modules I found on CPAN. For
|
---|
538 | benchmarking results, see L<Hash::Ordered::Benchmarks>.
|
---|
539 |
|
---|
540 | =head2 Tie modules
|
---|
541 |
|
---|
542 | The following modules offer some sort of tie interface. I don't like ties, in
|
---|
543 | general, because of the extra indirection involved over a direct method call, but
|
---|
544 | if you are willing to pay that penalty, you might want to try one of these.
|
---|
545 |
|
---|
546 | L<Tie::IxHash> is probably the most well known and includes an OO API. If its
|
---|
547 | warts and performance profile aren't a problem, it might serve.
|
---|
548 |
|
---|
549 | L<Tie::LLHash> I haven't used, but the linked-list implementation might be
|
---|
550 | worthwhile if you expect to do a lot of deletions.
|
---|
551 |
|
---|
552 | L<Tie::Hash::Indexed> is implemented in XS and thus seems promising if pure-Perl
|
---|
553 | isn't a criterion; it often fails tests on Perl 5.18 and above due to the hash
|
---|
554 | randomization change.
|
---|
555 |
|
---|
556 | These other modules have very specific designs/limitations and I didn't find
|
---|
557 | any of them suitable for general purpose use:
|
---|
558 |
|
---|
559 | =over 4
|
---|
560 |
|
---|
561 | =item *
|
---|
562 |
|
---|
563 | L<Tie::Array::AsHash> â array elements split with separator; tie API only
|
---|
564 |
|
---|
565 | =item *
|
---|
566 |
|
---|
567 | L<Tie::Hash::Array> â ordered alphabetically; tie API only
|
---|
568 |
|
---|
569 | =item *
|
---|
570 |
|
---|
571 | L<Tie::InsertOrderHash> â ordered by insertion; tie API only
|
---|
572 |
|
---|
573 | =item *
|
---|
574 |
|
---|
575 | L<Tie::StoredOrderHash> â ordered by last update; tie API only
|
---|
576 |
|
---|
577 | =back
|
---|
578 |
|
---|
579 | =head2 Other ordered hash modules
|
---|
580 |
|
---|
581 | Other modules stick with an object-oriented API, with a wide variety of
|
---|
582 | implementation approaches.
|
---|
583 |
|
---|
584 | L<Array::AsHash> is essentially an inverse implementation from Hash::Ordered.
|
---|
585 | It keeps pairs in an array and uses a hash to index into the array. I think
|
---|
586 | this indirection makes hash-like operations slower, but getting the list of
|
---|
587 | pairs back out is much faster. It takes an arrayref to initialize, but can
|
---|
588 | shallow copy it if needed. I think this is a reasonable alternative if static
|
---|
589 | construction and listing out contents is more common than individual item
|
---|
590 | access.
|
---|
591 |
|
---|
592 | These other modules have restrictions or particularly complicated
|
---|
593 | implementations (often relying on C<tie>) and thus I didn't think any of them
|
---|
594 | really suitable for use:
|
---|
595 |
|
---|
596 | =over 4
|
---|
597 |
|
---|
598 | =item *
|
---|
599 |
|
---|
600 | L<Array::Assign> â arrays with named access; restricted keys
|
---|
601 |
|
---|
602 | =item *
|
---|
603 |
|
---|
604 | L<Array::OrdHash> â overloads array/hash deref and uses internal tied data
|
---|
605 |
|
---|
606 | =item *
|
---|
607 |
|
---|
608 | L<Data::Pairs> â array of key-value hashrefs; allows duplicate keys
|
---|
609 |
|
---|
610 | =item *
|
---|
611 |
|
---|
612 | L<Data::OMap> â array of key-value hashrefs; no duplicate keys
|
---|
613 |
|
---|
614 | =item *
|
---|
615 |
|
---|
616 | L<Data::XHash> â blessed, tied hashref with doubly-linked-list
|
---|
617 |
|
---|
618 | =back
|
---|
619 |
|
---|
620 | =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
|
---|
621 |
|
---|
622 | =head1 SUPPORT
|
---|
623 |
|
---|
624 | =head2 Bugs / Feature Requests
|
---|
625 |
|
---|
626 | Please report any bugs or feature requests through the issue tracker
|
---|
627 | at L<https://github.com/dagolden/Hash-Ordered/issues>.
|
---|
628 | You will be notified automatically of any progress on your issue.
|
---|
629 |
|
---|
630 | =head2 Source Code
|
---|
631 |
|
---|
632 | This is open source software. The code repository is available for
|
---|
633 | public review and contribution under the terms of the license.
|
---|
634 |
|
---|
635 | L<https://github.com/dagolden/Hash-Ordered>
|
---|
636 |
|
---|
637 | git clone https://github.com/dagolden/Hash-Ordered.git
|
---|
638 |
|
---|
639 | =head1 AUTHOR
|
---|
640 |
|
---|
641 | David Golden <[email protected]>
|
---|
642 |
|
---|
643 | =head1 COPYRIGHT AND LICENSE
|
---|
644 |
|
---|
645 | This software is Copyright (c) 2014 by David Golden.
|
---|
646 |
|
---|
647 | This is free software, licensed under:
|
---|
648 |
|
---|
649 | The Apache License, Version 2.0, January 2004
|
---|
650 |
|
---|
651 | =cut
|
---|