File Coverage

lib/Ruby/Collections/Hash.pm
Criterion Covered Total %
statement 481 505 95.2
branch 186 282 65.9
condition 12 27 44.4
subroutine 77 78 98.7
pod 65 68 95.5
total 821 960 85.5


line stmt bran cond sub pod time code
1             package Ruby::Collections::Hash;
2 3     3   3515 use Tie::Hash;
  3         3591  
  3         145  
3             our @ISA = 'Tie::StdHash';
4 3     3   24 use strict;
  3         5  
  3         110  
5 3     3   61 use v5.10;
  3         12  
  3         161  
6 3     3   17 use Scalar::Util qw(reftype);
  3         7  
  3         166  
7 3     3   39 use FindBin;
  3         6  
  3         134  
8 3     3   16 use lib "$FindBin::Bin/../../../lib";
  3         5  
  3         32  
9 3     3   2789 use Ruby::Collections::OrderedHash;
  3         10  
  3         92  
10 3     3   19 use Ruby::Collections;
  3         7  
  3         388  
11             use overload (
12 3         60 '==' => \&eql,
13             'eq' => \&eql,
14             '!=' => \¬_eql,
15             'ne' => \¬_eql,
16             '""' => \&to_s
17 3     3   19 );
  3         7  
18              
19             sub TIEHASH {
20 153     153   262 my $class = shift;
21              
22 153         653 my $hash = tie my %hash, 'Ruby::Collections::OrderedHash';
23              
24 153         630 bless \%hash, $class;
25             }
26              
27             =item has_all()
28             Return 1.
29             If block is given, return 1 if all results are true,
30             otherwise 0.
31            
32             rh()->has_all # return 1
33             rh(1, 2, 3)->has_all # return 1
34             rh(2, 4, 6)->has_all( sub { $_[0] % 2 == 1 } ) # return 0
35             =cut
36              
37             sub has_all {
38 2     2 1 5 my ( $self, $block ) = @_;
39 2 50       7 ref($self) eq __PACKAGE__ or die;
40              
41 2 100       6 if ( defined $block ) {
42 1         8 while ( my ( $key, $val ) = each %$self ) {
43 1 50       4 return 0 if ( not $block->( $key, $val ) );
44             }
45             }
46              
47 1         9 return 1;
48             }
49              
50             =item has_any()
51             Check if any entry exists.
52             When block given, check if any result returned by block are true.
53            
54             rh( 1 => 2 )->has_any # return 1
55             rh->has_any # return 0
56             rh ( 2 => 4, 6 => 8 )->has_any( sub { $_[0] % 2 == 1 } ) # return 0
57             =cut
58              
59             sub has_any {
60 3     3 1 6 my ( $self, $block ) = @_;
61 3 50       8 ref($self) eq __PACKAGE__ or die;
62              
63 3 100       7 if ( defined $block ) {
64 1         4 while ( my ( $key, $val ) = each %$self ) {
65 2 50       6 return 1 if ( $block->( $key, $val ) );
66             }
67 1         5 return 0;
68             }
69             else {
70 2 100       11 return $self->size > 0 ? 1 : 0;
71             }
72             }
73              
74             =item assoc()
75             Find the key and return the key-value pair in a Ruby::Collections::Array.
76             Return undef if key is not found.
77            
78             rh( 'a' => 123, 'b' => 456 )->assoc('b') # return [ 'b', 456 ]
79             rh( 'a' => 123, 'b' => 456 )->assoc('c') # return undef
80             =cut
81              
82             sub assoc {
83 2     2 1 4 my ( $self, $obj ) = @_;
84 2 50       7 ref($self) eq __PACKAGE__ or die;
85              
86 2 100       10 if ( $self->{$obj} ) {
87 1         5 return ra( $obj, $self->{$obj} );
88             }
89             else {
90 1         7 return undef;
91             }
92             }
93              
94             =item chunk()
95             Chunk consecutive elements which is under certain condition
96             into [ condition, [ [ key, value ]... ] ] array.
97            
98             rh( 1 => 1, 2 => 2, 3 => 3, 5 => 5, 4 => 4 )->chunk( sub { $_[0] % 2 } )
99             #return [ [ 1, [ [ 1, 1 ] ] ],
100             [ 0, [ [ 2, 2 ] ] ],
101             [ 1, [ [ 3, 3 ], [ 5, 5 ] ] ],
102             [ 0, [ [ 4, 4 ] ] ] ]
103             =cut
104              
105             sub chunk {
106 1     1 1 3 my ( $self, $block ) = @_;
107 1 50       6 ref($self) eq __PACKAGE__ or die;
108              
109 1         5 my $new_ary = tie my @new_ary, 'Ruby::Collections::Array';
110 1         6 my $prev = undef;
111 1         5 my $chunk = tie my @chunk, 'Ruby::Collections::Array';
112 1         6 my $i = 0;
113              
114 1         6 while ( my ( $k, $v ) = each %$self ) {
115 5         12 my $key = $block->( $k, $v );
116 5 100       27 if ( p_obj($key) eq p_obj($prev) ) {
117 1         5 $chunk->push( ra( $k, $v ) );
118             }
119             else {
120 4 100       11 if ( $i != 0 ) {
121 3         10 my $sub_ary = tie my @sub_ary, 'Ruby::Collections::Array';
122 3         19 $sub_ary->push( $prev, $chunk );
123 3         9 $new_ary->push($sub_ary);
124             }
125 4         5 $prev = $key;
126 4         15 $chunk = tie my @chunk, 'Ruby::Collections::Array';
127 4         23 $chunk->push( ra( $k, $v ) );
128             }
129 5         35 $i++;
130             }
131 1 50       20 if ( $chunk->has_any ) {
132 1         6 my $sub_ary = tie my @sub_ary, 'Ruby::Collections::Array';
133 1         8 $sub_ary->push( $prev, $chunk );
134 1         3 $new_ary->push($sub_ary);
135             }
136              
137 1         13 return $new_ary;
138             }
139              
140             =item claer()
141             Clear all keys and values.
142             =cut
143              
144             sub clear {
145 1     1 0 6 my ($self) = @_;
146 1 50       6 ref($self) eq __PACKAGE__ or die;
147              
148 1         5 %$self = ();
149              
150 1         2 return $self;
151             }
152              
153             =item delete()
154             Delete the kry-value pair by key, return the value after deletion.
155             If block is given, passing the value after deletion
156             and return the result of block.
157            
158             rh( 'a' => 1 )->delete('a') # return 1
159             rh( 'a' => 1 )->delete('b') # return undef
160             rh( 'a' => 1 )->delete( 'a', sub{ $_[0] * 3 } ) # return 3
161             =cut
162              
163             sub delete {
164 3     3 1 7 my ( $self, $key, $block ) = @_;
165 3 50       8 ref($self) eq __PACKAGE__ or die;
166              
167 3 100       8 if ( defined $block ) {
168 1         4 return $block->( delete $self->{$key} );
169             }
170             else {
171 2         12 return delete $self->{$key};
172             }
173             }
174              
175             =item count()
176             Count the number of key-value pairs.
177             If block is given, count the number of results returned by
178             the block which are true.
179            
180             rh( 'a' => 'b', 'c' => 'd' )->count # return 2
181             rh( 1 => 3, 2 => 4, 5 => 6 )->count( sub {
182             my ( $key, $val ) = @_;
183             $key % 2 == 0 && $val % 2 == 0;
184             } )
185             # return 1
186             =cut
187              
188             sub count {
189 2     2 1 3 my ( $self, $ary_or_block ) = @_;
190 2 50       9 ref($self) eq __PACKAGE__ or die;
191              
192 2         3 my $count = 0;
193 2 100       8 if ( defined $ary_or_block ) {
194 1 50       5 if ( ref($ary_or_block) eq 'CODE' ) {
    0          
195 1         4 while ( my ( $key, $val ) = each %$self ) {
196 3 100       7 if ( $ary_or_block->( $key, $val ) ) {
197 1         11 $count++;
198             }
199             }
200             }
201             elsif ( reftype($ary_or_block) eq 'ARRAY' ) {
202 0         0 while ( my ( $key, $val ) = each %$self ) {
203 0 0 0     0 if ( p_obj( @{$ary_or_block}[0] ) eq p_obj($key)
  0         0  
  0         0  
204             && p_obj( @{$ary_or_block}[1] ) eq p_obj($val) )
205             {
206 0         0 $count++;
207             }
208             }
209             }
210             }
211             else {
212 1         5 return $self->length;
213             }
214              
215 1         5 return $count;
216             }
217              
218             =item cycle()
219             Apply the block with each key-value pair repeatedly.
220             If a limit is given, it only repeats limit of cycles.
221            
222             rh( 1 => 2, 3 => 4 )->cycle( sub { print "$_[0], $_[1], " } )
223             # print 1, 2, 3, 4, 1, 2, 3, 4... forever
224            
225             rh( 1 => 2, 3 => 4 )->cycle( 1, sub { print "$_[0], $_[1], " } )
226             # print 1, 2, 3, 4,
227             =cut
228              
229             sub cycle {
230 2     2 1 3 my $self = shift @_;
231 2 50       7 ref($self) eq __PACKAGE__ or die;
232              
233 2 50       8 if ( @_ == 1 ) {
    100          
234 0         0 my ($block) = @_;
235 0         0 while (1) {
236 0         0 while ( my ( $key, $val ) = each %$self ) {
237 0         0 $block->( $key, $val );
238             }
239             }
240             }
241             elsif ( @_ == 2 ) {
242 1         2 my ( $n, $block ) = @_;
243 1         7 for ( my $i = 0 ; $i < $n ; $i++ ) {
244 1         4 while ( my ( $key, $val ) = each %$self ) {
245 2         6 $block->( $key, $val );
246             }
247             }
248             }
249             else {
250 1         16 die 'ArgumentError: wrong number of arguments ('
251             . scalar(@_)
252             . ' for 0..1)';
253             }
254             }
255              
256             =item delete_if()
257             Pass all key-value pairs into the block and remove them out of self
258             if the results returned by the block are true.
259            
260             rh( 1 => 3, 2 => 4 )->delete_if( sub {
261             my ( $key, $val ) = @_;
262             $key % 2 == 1;
263             } )
264             # return { 2 => 4 }
265             =cut
266              
267             sub delete_if {
268 3     3 1 11 my ( $self, $block ) = @_;
269 3 50       11 ref($self) eq __PACKAGE__ or die;
270              
271 3         12 while ( my ( $key, $val ) = each %$self ) {
272 4 100       11 if ( $block->( $key, $val ) ) {
273 2         15 delete $self->{$key};
274             }
275             }
276              
277 3         7 return $self;
278             }
279              
280             =item drop()
281             Remove the first n key-value pair and store rest of elements
282             in a new Ruby::Collections::Array.
283            
284             rh( 1 => 2, 3 => 4, 5 => 6)->drop(1) # return [ [ 3, 4 ], [ 5, 6 ] ]
285             =cut
286              
287             sub drop {
288 2     2 1 5 my ( $self, $n ) = @_;
289 2 50       7 ref($self) eq __PACKAGE__ or die;
290              
291 2 100       24 die 'ArgumentError: attempt to drop negative size' if ( $n < 0 );
292              
293 1         3 my $new_ary = ra;
294 1         2 my $index = 0;
295 1         5 while ( my ( $key, $val ) = each %$self ) {
296 3 100       8 if ( $n <= $index ) {
297 2         6 $new_ary->push( ra( $key, $val ) );
298             }
299 3         14 $index++;
300             }
301              
302 1         7 return $new_ary;
303             }
304              
305             =item drop_while()
306             Remove the first n key-value pair until the result returned by
307             the block is true and store rest of elements in a new Ruby::Collections::Array.
308            
309             rh( 0 => 2, 1 => 3, 2 => 4, 5 => 7)->drop_while( sub {
310             my ( $key, $val ) = @_;
311             $key % 2 == 1;
312             } )
313             # return [ [ 1, 3 ], [ 2, 4 ], [ 5, 7 ] ]
314             =cut
315              
316             sub drop_while {
317 1     1 1 11 my ( $self, $block ) = @_;
318 1 50       4 ref($self) eq __PACKAGE__ or die;
319              
320 1         4 my $new_ary = ra;
321 1         2 my $cut_point = 0;
322 1         5 while ( my ( $key, $val ) = each %$self ) {
323 4 100 100     11 if ( $block->( $key, $val ) || $cut_point ) {
324 3         21 $cut_point = 1;
325 3         8 $new_ary->push( ra( $key, $val ) );
326             }
327             }
328              
329 1         8 return $new_ary;
330             }
331              
332             =item each()
333             Iterate each key-value pair and pass it to the block
334             one by one. Return self.
335             Alias: each_entry(), each_pair()
336            
337             rh( 1 => 2, 3 => 4)->each( sub {
338             my ( $key, $val ) = @_;
339             print "$key, $val, "
340             } )
341             # print 1, 2, 3, 4,
342             =cut
343              
344             sub each {
345 6     6 1 13 my ( $self, $block ) = @_;
346 6 50       17 ref($self) eq __PACKAGE__ or die;
347              
348 6         25 while ( my ( $key, $val ) = each %$self ) {
349 12         29 $block->( $key, $val );
350             }
351              
352 6         26 return $self;
353             }
354              
355             *each_entry = \&each;
356              
357             *each_pair = \&each;
358              
359             =item each_cons()
360             Iterates each key-value pair([ k, v ]) as array of consecutive elements.
361            
362             rh( 1 => 2, 3 => 4, 5 => 6 )->each_cons( 2, sub{
363             my ($sub_ary) = @_;
364             p $sub_ary[0]->zip($sub_ary[1]);
365             } )
366             # print "[[1, 3], [2, 4]]\n[[3, 5], [4, 6]]\n"
367             =cut
368              
369             sub each_cons {
370 2     2 1 5 my ( $self, $n, $block ) = @_;
371 2 50       6 ref($self) eq __PACKAGE__ or die;
372              
373 2         7 return $self->to_a->each_cons( $n, $block );
374             }
375              
376             =item each_slice()
377             Put each key and value into a Ruby::Collections::Array and chunk them
378             into other Ruby::Collections::Array(s) of size n.
379            
380             rh( 1 => 2, 3 => 4, 5 => 6 )->each_slice(2)
381             # return [ [ [ 1, 2 ], [ 3, 4] ], [ [ 5, 6 ] ] ]
382             =cut
383              
384             sub each_slice {
385 2     2 1 4 my ( $self, $n, $block ) = @_;
386 2 50       7 ref($self) eq __PACKAGE__ or die;
387              
388 2         7 return $self->to_a->each_slice( $n, $block );
389             }
390              
391             =item each_key()
392             Put each key in to a Ruby::Collections::Array.
393            
394             rh( 1 => 2, 'a' => 'b', [ 3, { 'c' => 'd' } ] => 4 ).each_key( sub {
395             print "$_[0], "
396             } )
397             # print "1, a, [3, {c=>d}], "
398             =cut
399              
400             sub each_key {
401 1     1 1 3 my ( $self, $block ) = @_;
402 1 50       5 ref($self) eq __PACKAGE__ or die;
403              
404 1         6 for my $key ( keys %$self ) {
405 3         86 $block->($key);
406             }
407              
408 1         20 return $self;
409             }
410              
411             =item each_value()
412             Put each value in to a Ruby::Collections::Array.
413            
414             rh( 1 => 2, 'a' => undef, '3' => rh( [2] => [3] ) )->each_value( sub {
415             print "$_[0], "
416             } )
417             # print "2, undef, {[2]=>[3]}, "
418             =cut
419              
420             sub each_value {
421 1     1 1 3 my ( $self, $block ) = @_;
422 1 50       4 ref($self) eq __PACKAGE__ or die;
423              
424 1         5 for my $val ( values %$self ) {
425 3         8 $block->($val);
426             }
427              
428 1         7 return $self;
429             }
430              
431             =item each_with_index()
432             Iterate each key-value pair and pass it with index to the block
433             one by one. Return self.
434            
435             rh( 'a' => 'b', 'c' => 'd' )->each_with_index( sub {
436             my ( $key, $val, $index ) = @_;
437             print "$key, $val, $index, "
438             } )
439             # print "a, b, 0, c, d, 1, "
440             =cut
441              
442             sub each_with_index {
443 1     1 1 4 my ( $self, $block ) = @_;
444 1 50       6 ref($self) eq __PACKAGE__ or die;
445              
446 1 50       6 if ( defined $block ) {
447 1         2 my $index = 0;
448 1         6 while ( my ( $key, $val ) = each %$self ) {
449 2         6 $block->( $key, $val, $index );
450 2         73 $index++;
451             }
452             }
453              
454 1         5 return $self;
455             }
456              
457             =item each_with_object()
458             Iterate each key-value pair and pass it with an object to the block
459             one by one. Return the object.
460            
461             my $ra = ra;
462             rh( 1 => 2, 3 => 4 )->each_with_object( $ra, sub {
463             my ( $key, $val, $obj ) = @_;
464             $obj->push( $key, $val );
465             } );
466             p $ra;
467             # print "[1, 2, 3, 4]\n"
468             =cut
469              
470             sub each_with_object {
471 1     1 1 3 my ( $self, $object, $block ) = @_;
472 1 50       5 ref($self) eq __PACKAGE__ or die;
473              
474 1 50       5 if ( defined $block ) {
475 1         5 while ( my ( $key, $val ) = each %$self ) {
476 2         6 $block->( $key, $val, $object );
477             }
478             }
479              
480 1         4 return $object;
481             }
482              
483             =item is_empty()
484             Check if Ruby::Collections::Hash is empty or not.
485            
486             rh()->is_empty # return 1
487             rh( 1 => 2 )->is_empty # return 0
488             =cut
489              
490             sub is_empty {
491 3     3 1 6 my ($self) = @_;
492 3 50       9 ref($self) eq __PACKAGE__ or die;
493              
494 3 100       12 return scalar( keys %$self ) == 0 ? 1 : 0;
495             }
496              
497             =item entries()
498             Put each key-value pair to a Ruby::Collections::Array.
499            
500             rh( 1 => 2, 3 => 4)->entries # return [ [ 1, 2 ], [ 3, 4 ] ]
501             =cut
502              
503             sub entries {
504 2     2 1 7 my ($self) = @_;
505 2 50       11 ref($self) eq __PACKAGE__ or die;
506              
507 2         12 return $self->to_a;
508             }
509              
510             =item eql
511             Check if contents of both hashes are the same. Key order is not matter.
512            
513             rh( 1 => 2, 3 => 4 )->eql( { 3 => 4, 1 => 2 } ) # return 1
514             rh( [1] => 2, 3 => 4 )->eql( { 3 => 4, [1] => 2 } ) # return 0
515             rh( [1] => 2, 3 => 4 )->eql( rh( 3 => 4, [1] => 2 ) ) # return 1
516             =cut
517              
518             sub eql {
519 25     25 1 9720 my ( $self, $other ) = @_;
520 25 50       98 ref($self) eq __PACKAGE__ or die;
521              
522 25 50       113 if ( reftype($other) eq 'HASH' ) {
523 25         124 while ( my ( $key, $val ) = each %$self ) {
524 44 100       125 if ( p_obj($val) ne p_obj( $other->{$key} ) ) {
525 1         6 return 0;
526             }
527             }
528             }
529             else {
530 0         0 return 0;
531             }
532              
533 24         105 return 1;
534             }
535              
536             sub not_eql {
537 0     0 0 0 my ( $self, $other ) = @_;
538 0 0       0 ref($self) eq __PACKAGE__ or die;
539              
540 0 0       0 return $self->eql($other) == 0 ? 1 : 0;
541             }
542              
543             =item fetch()
544             Retrieve the value by certain key. Throw an exception if key is not found.
545             If default value is given, return the default value when key is not found.
546             If block is given, pass the key into the block and return the result when
547             key is not found.
548            
549             rh( 1 => 2, 3 => 4 )->fetch(1) # return 2
550             rh( 1 => 2, 3 => 4 )->fetch( 5, 10 ) # return 10
551             rh( 1 => 2, 3 => 4 )->fetch( 5, sub { $_[0] * $_[0] } ) # return 25
552             =cut
553              
554             sub fetch {
555 4     4 1 8 my ( $self, $key, $default_or_block ) = @_;
556 4 50       44 ref($self) eq __PACKAGE__ or die;
557              
558 4         20 my $val = $self->{$key};
559 4 100       15 if ( defined $val ) {
560 1         5 return $val;
561             }
562             else {
563 3 100       7 if ( defined $default_or_block ) {
564 2 100       7 if ( ref($default_or_block) eq 'CODE' ) {
565 1         5 return $default_or_block->($key);
566             }
567             else {
568 1         6 return $default_or_block;
569             }
570             }
571             else {
572 1         14 die 'KeyError: key not found: ' . $key;
573             }
574             }
575             }
576              
577             =item find()
578             Find the first key-value pair which result returned by
579             the block is true. If default is given, return the default
580             when such pair can't be found.
581             Alias: detect()
582            
583             rh( 'a' => 1, 'b' => 2 )->find( sub {
584             my ( $key, $val ) = @_;
585             $val % 2 == 0;
586             } )
587             # return [ 'b', 2 ]
588            
589             rh( 'a' => 1, 'b' => 2 )->detect( sub { 'Not Found!' }, sub {
590             my ( $key, $val ) = @_;
591             $val % 2 == 3;
592             } )
593             # return 'Not Found!'
594             =cut
595              
596             sub find {
597 3     3 1 7 my $self = shift @_;
598 3 50       11 ref($self) eq __PACKAGE__ or die;
599              
600 3 100       11 if ( @_ == 1 ) {
    100          
601 1         2 my ($block) = @_;
602 1         6 while ( my ( $key, $val ) = each %$self ) {
603 2 100       7 if ( $block->( $key, $val ) ) {
604 1         10 return ra( $key, $val );
605             }
606             }
607             }
608             elsif ( @_ == 2 ) {
609 1         2 my ( $default, $block ) = @_;
610 1         5 while ( my ( $key, $val ) = each %$self ) {
611 2 50       8 if ( $block->( $key, $val ) ) {
612 0         0 return ra( $key, $val );
613             }
614             }
615 1         4 return $default->();
616             }
617             else {
618 1         11 die 'ArgumentError: wrong number of arguments ('
619             . scalar(@_)
620             . ' for 0..1)';
621             }
622              
623 0         0 return undef;
624             }
625              
626             *detect = \&find;
627              
628             =item find_all()
629             Pass each key-value pair to the block and store all elements
630             which are true returned by the block to a Ruby::Collections::Array.
631            
632             rh( 'a' => 'b', 1 => 2, 'c' => 'd', 3 => '4')->select(
633             sub {
634             my ( $key, $val ) = @_;
635             looks_like_number($key) && looks_like_number($val);
636             }
637             )
638             # return [ [ 1, 2 ], [ 3, 4 ] ]
639             =cut
640              
641             sub find_all {
642 1     1 1 2 my ( $self, $block ) = @_;
643 1 50       5 ref($self) eq __PACKAGE__ or die;
644              
645 1         4 my $new_ary = ra;
646 1         4 while ( my ( $key, $val ) = each %$self ) {
647 4 100       10 if ( $block->( $key, $val ) ) {
648 1         9 $new_ary->push( ra( $key, $val ) );
649             }
650             }
651              
652 1         6 return $new_ary;
653             }
654              
655             =item find_index()
656             Find the position of pair under certain condition. Condition can be
657             an array which contains the target key & value or can be a block.
658            
659             rh( 1 => 2, 3 => 4 )->find_index( [ 5, 6 ] ) # return undef
660             rh( 1 => 2, 3 => 4 )->find_index( [ 3, 4 ] ) # return 1
661             rh( 1 => 2, 3 => 4 )->find_index( sub { $_[0] == 1 } ) # return 0
662             =cut
663              
664             sub find_index {
665 3     3 1 7 my ( $self, $ary_or_block ) = @_;
666 3 50       11 ref($self) eq __PACKAGE__ or die;
667              
668 3 100       16 if ( reftype($ary_or_block) eq 'ARRAY' ) {
    50          
669 2         3 my $index = 0;
670 2         10 while ( my ( $key, $val ) = each %$self ) {
671 4 100 66     6 if ( p_obj( @{$ary_or_block}[0] ) eq p_obj($key)
  4         13  
  1         5  
672             && p_obj( @{$ary_or_block}[1] ) eq p_obj($val) )
673             {
674 1         7 return $index;
675             }
676 3         21 $index++;
677             }
678             }
679             elsif ( ref($ary_or_block) eq 'CODE' ) {
680 1         2 my $index = 0;
681 1         6 while ( my ( $key, $val ) = each %$self ) {
682 2 100       6 if ( $ary_or_block->( $key, $val ) ) {
683 1         9 return $index;
684             }
685 1         13 $index++;
686             }
687             }
688              
689 1         5 return undef;
690             }
691              
692             =item first()
693             Return the first element. If n is specified, return the first n elements.
694            
695             rh( 1 => 2, 3 => 4)->first # return [ [ 1, 2 ] ]
696             rh( 1 => 2, 3 => 4)->first(5) # return [ [ 1, 2 ], [ 3, 4 ] ]
697             =cut
698              
699             sub first {
700 3     3 1 5 my ( $self, $n ) = @_;
701 3 50       10 ref($self) eq __PACKAGE__ or die;
702              
703 3 100       7 if ( defined $n ) {
704 2 100       21 die 'ArgumentError: negative array size' if ( $n < 0 );
705              
706 1         3 my $new_ary = ra;
707 1         6 while ( my ( $key, $val ) = each %$self ) {
708 2 50       7 if ( $n <= 0 ) {
709 0         0 return $new_ary;
710             }
711 2         6 $new_ary->push( ra( $key, $val ) );
712 2         14 $n--;
713             }
714 1         8 return $new_ary;
715             }
716             else {
717 1         5 while ( my ( $key, $val ) = each %$self ) {
718 1         4 return ra( $key, $val );
719             }
720 0         0 return undef;
721             }
722             }
723              
724             =item flat_map()
725             Call map(), then call flatten(1).
726             Alias: collect_concat()
727            
728             rh( 1 => 2, 3 => 4 )->flat_map(
729             sub {
730             my ( $key, $val ) = @_;
731             [ $key * $val ];
732             }
733             )
734             # return [ 2, 12 ]
735             =cut
736              
737             sub flat_map {
738 2     2 1 3 my ( $self, $block ) = @_;
739 2 50       10 ref($self) eq __PACKAGE__ or die;
740              
741 2         8 my $new_ary = $self->map($block);
742 2         11 $new_ary->flattenEx(1);
743              
744 2         11 return $new_ary;
745             }
746              
747             *collect_concat = \&flat_map;
748              
749             =item flatten()
750             Push each key & value into a Ruby::Collections::Array. If n is specified,
751             call flatten( n - 1 ) on the Ruby::Collections::Array.
752            
753             rh( 1 => [ 2, 3 ], 4 => 5 )->flatten # return [ 1, [ 2, 3 ], 4, 5 ]
754             rh( 1 => [ 2, 3 ], 4 => 5 )->flatten(2) # return [ 1, 2, 3, 4, 5 ]
755             =cut
756              
757             sub flatten {
758 2     2 1 5 my ( $self, $n ) = @_;
759 2 50       7 ref($self) eq __PACKAGE__ or die;
760              
761 2         8 my $new_ary = ra();
762 2         10 while ( my ( $key, $val ) = each %$self ) {
763 4         13 $new_ary->push( $key, $val );
764             }
765              
766 2 100 66     15 if ( defined $n && $n >= 2 ) {
767 1         5 $new_ary->flattenEx( $n - 1 );
768             }
769              
770 2         11 return $new_ary;
771             }
772              
773             =item grep()
774             Using regex to match elements and store them in a Ruby::Collecitons::Array.
775             If block is given, transform each element by the block.
776             Note: This implementation is different from Ruby due to the missing of ===
777             operator in Perl.
778            
779             rh( 'a' => 1, '2' => 'b', 'c' => 3 )->grep(qr/^\[[a-z]/) # return [[a, 1], [c, 3]]
780             rh( 'a' => 1, '2' => 'b', 'c' => 3 )->grep( qr/^\[[a-z]/, sub {
781             $_[0] << 'z';
782             })
783             # return [[a, 1, z], [c, 3, z]]
784             =cut
785              
786             sub grep {
787 2     2 1 4 my ( $self, $pattern, $block ) = @_;
788 2 50       8 ref($self) eq __PACKAGE__ or die;
789              
790 2         8 return $self->to_a->grep( $pattern, $block );
791             }
792              
793             =item group_by()
794             Group each element by the result of block, store them in a Ruby::Collections::Hash.
795            
796             rh( 1 => 3, 0 => 4, 2 => 5 )->group_by(sub {
797             $_[0] + $_[1]
798             })
799             #return
800             =cut
801              
802             sub group_by {
803 1     1 1 4 my ( $self, $block ) = @_;
804 1 50       5 ref($self) eq __PACKAGE__ or die;
805              
806 1         4 my $new_hash = rh;
807 1         5 while ( my ( $key, $val ) = each %$self ) {
808 3         9 my $group = $block->( $key, $val );
809 3 100       21 if ( defined $new_hash->{$group} ) {
810 1         6 $new_hash->{$group}->push( ra( $key, $val ) );
811             }
812             else {
813 2         6 $new_hash->{$group} = ra;
814 2         10 $new_hash->{$group}->push( ra( $key, $val ) );
815             }
816             }
817              
818 1         11 return $new_hash;
819             }
820              
821             =item include()
822             Check if key exists.
823             Alias: has_key(), has_member()
824            
825             rh( 1 => 2, [ 3, { 4 => 5 } ] => 5, undef => 6 )->include(1) # return 1
826             rh( 1 => 2, [ 3, { 4 => 5 } ] => 6, undef => 7 )->has_key([ 3, { 4 => 5 } ]) # return 1
827             rh( 1 => 2, [ 3, { 4 => 5 } ] => 5, undef => 6 )->has_member(undef) # return 1
828             rh( 1 => 2, [ 3, { 4 => 5 } ] => 5, undef => 6 )->include(7) # return 0
829             =cut
830              
831             sub include {
832 4     4 1 7 my ( $self, $key ) = @_;
833 4 50       15 ref($self) eq __PACKAGE__ or die;
834              
835 4         15 return ra( keys %$self )->include($key);
836             }
837              
838             *has_key = \&include;
839              
840             *has_member = \&include;
841              
842             =item inject()
843             Passing the result of block by each iteration to next iteration, return the
844             final result in the end.
845             Alias: reduce()
846            
847             rh( 1 => 2, 3 => 4, 5 => 6 )->inject( sub {
848             my ( $o, $i ) = @_;
849             @$o[0] += @$i[0];
850             @$o[1] += @$i[1];
851             $o;
852             })
853             # return [ 9, 12 ]
854             rh( 1 => 2, 3 => 4, 5 => 6 )->inject( [ 7, 7 ], sub {
855             my ( $o, $i ) = @_;
856             @$o[0] += @$i[0];
857             @$o[1] += @$i[1];
858             $o;
859             })
860             # return [ 16, 19 ]
861             =cut
862              
863             sub inject {
864 2     2 1 5 my $self = shift @_;
865 2 50       7 ref($self) eq __PACKAGE__ or die;
866              
867 2         9 return $self->to_a->inject(@_);
868             }
869              
870             *reduce = \&inject;
871              
872             =item inspect()
873             Return the data structure in string form of self.
874             Alias: to_s()
875            
876             rh( [ 1, 2 ] => 3, 'a' => 'b' )->inspect # return { [ 1, 2 ] => 3, a => b }
877             =cut
878              
879             sub inspect {
880 1     1 1 3 my ($self) = @_;
881 1 50       6 ref($self) eq __PACKAGE__ or die;
882              
883 1         5 return p_hash $self;
884             }
885              
886             *to_s = \&inspect;
887              
888             =item
889             Invert the whole hash. Let values be the keys and keys be the values.
890            
891             rh( 1 => 'a', 2 => 'b', 3 => 'a' )->invert # return { a => 3, b => 2 }
892             =cut
893              
894             sub invert {
895 1     1 0 3 my ($self) = @_;
896 1 50       5 ref($self) eq __PACKAGE__ or die;
897              
898 1         5 my $new_hash = rh;
899 1         4 while ( my ( $key, $val ) = each %$self ) {
900 3         15 $new_hash->{$val} = $key;
901             }
902              
903 1         5 return $new_hash;
904             }
905              
906             =item keep_if()
907             Pass all key-value pairs to the block and only keep the elements which get the results
908             returned by the block are true.
909            
910             rh( 1 => 1, 2 => 2, 3 => 3 )->keep_if( sub { $_[0] % 2 == 1 } ) # return { 1 => 1, 3 => 3 }
911             =cut
912              
913             sub keep_if {
914 1     1 1 2 my ( $self, $block ) = @_;
915 1 50       6 ref($self) eq __PACKAGE__ or die;
916              
917 1         6 while ( my ( $key, $val ) = each %$self ) {
918 3 100       8 if ( not $block->( $key, $val ) ) {
919 1         10 delete $self->{$key};
920             }
921             }
922              
923 1         6 return $self;
924             }
925              
926             =item key()
927             Find the key by value.
928            
929             rh( 1 => 2, 3 => 2 )->key(2) # return 1
930             rh( 1 => 2, 3 => 2 )->key(4) # return undef
931             =cut
932              
933             sub key {
934 2     2 1 5 my ( $self, $value ) = @_;
935 2 50       8 ref($self) eq __PACKAGE__ or die;
936              
937 2         9 while ( my ( $key, $val ) = each %$self ) {
938 3 100       8 if ( p_obj($value) eq p_obj($val) ) {
939 1         8 return $key;
940             }
941             }
942              
943 1         6 return undef;
944             }
945              
946             =item keys()
947             Put all keys in a Ruby::Collections::Array.
948            
949             rh( 1 => 2, 3 => 4, 5 => 6 )->keys # return [ 1, 3, 5 ]
950             =cut
951              
952             sub keys {
953 1     1 1 3 my ($self) = @_;
954 1 50       5 ref($self) eq __PACKAGE__ or die;
955              
956 1         5 return ra( keys %$self );
957             }
958              
959             =item length()
960             Return the number of key-value pairs.
961             Alias: size()
962            
963             rh->length # return 0
964             rh( 1 => 2, 3 => 4)->size # return 2
965             =cut
966              
967             sub length {
968 13     13 1 19 my ($self) = @_;
969 13 50       37 ref($self) eq __PACKAGE__ or die;
970              
971 13         53 return scalar( keys %$self );
972             }
973              
974             *size = \&length;
975              
976             =item map()
977             Transform each key-value pair and store them into a new Ruby::Collections::Array.
978             Alias: collect()
979            
980             rh( 1 => 2, 3 => 4 )->map(
981             sub {
982             my ( $key, $val ) = @_;
983             $key * $val;
984             }
985             )
986             # return [ 2, 12 ]
987             =cut
988              
989             sub map {
990 4     4 1 8 my ( $self, $block ) = @_;
991 4 50       12 ref($self) eq __PACKAGE__ or die;
992              
993 4         11 my $new_ary = ra;
994 4         18 while ( my ( $key, $val ) = each %$self ) {
995 8         20 $new_ary->push( $block->( $key, $val ) );
996             }
997              
998 4         15 return $new_ary;
999             }
1000              
1001             *collect = \↦
1002              
1003             =item max()
1004             Find the max element of a Ruby::Collections::Hash.
1005             transform each element to scalar then compare it.
1006            
1007             rh( 6 => 5, 11 => 3, 2 => 1 )->max # return [ 6, 5 ]
1008             rh( 6 => 5, 11 => 3, 2 => 1 )->max( sub { @{$_[0]}[0] <=> @{$_[1]}[0] } ) # return [ 11, 3 ]
1009             =cut
1010              
1011             sub max {
1012 2     2 1 5 my ( $self, $block ) = @_;
1013 2 50       8 ref($self) eq __PACKAGE__ or die;
1014              
1015 2         9 return $self->to_a->max($block);
1016             }
1017              
1018             =item max_by()
1019             Transform all elements by the given block and then find the max.
1020             Return the element which is the origin of the max.
1021            
1022             rh( 6 => 5, 11 => 3, 2 => 20 )->max_by( sub { @{$_[0]}[0] + @{$_[0]}[1] } ) # return [ 2, 20 ]
1023             =cut
1024              
1025             sub max_by {
1026 1     1 1 2 my ( $self, $block ) = @_;
1027 1 50       6 ref($self) eq __PACKAGE__ or die;
1028              
1029 1         6 return $self->to_a->max_by($block);
1030             }
1031              
1032             =item merge()
1033             Merge all key-value pairs of other hash with self elements into a
1034             new Ruby::Collections::Hash.
1035            
1036             rh( 1 => 2, 3 => 4 )->merge( { 3 => 3, 4 => 5 } ) # return { 1 => 2, 3 => 3, 4 => 5 }
1037             =cut
1038              
1039             sub merge {
1040 1     1 1 3 my ( $self, $other_hash, $block ) = @_;
1041 1 50       5 ref($self) eq __PACKAGE__ or die;
1042              
1043 1         4 my $new_hash = rh($self);
1044 1         9 while ( my ( $key, $val ) = each %$other_hash ) {
1045 2 50 33     9 if ( defined $block && $self->{$key} && $other_hash->{$key} ) {
      33        
1046 0         0 $new_hash->{$key} =
1047             $block->( $key, $self->{$key}, $other_hash->{$key} );
1048             }
1049             else {
1050 2         9 $new_hash->{$key} = $val;
1051             }
1052             }
1053              
1054 1         6 return $new_hash;
1055             }
1056              
1057             *update = \&merge;
1058              
1059             =item mergeEx()
1060             Merge all key-value pairs of other hash with self elements and save result into self.
1061            
1062             rh( 1 => 2, 3 => 4 )->mergeEx( { 3 => 3, 4 => 5 } ) # return { 1 => 2, 3 => 3, 4 => 5 }
1063             =cut
1064              
1065             sub mergeEx {
1066 1     1 1 9 my ( $self, $other_hash, $block ) = @_;
1067 1 50       6 ref($self) eq __PACKAGE__ or die;
1068              
1069 1         8 while ( my ( $key, $val ) = each %$other_hash ) {
1070 2 50 33     25 if ( defined $block && $self->{$key} && $other_hash->{$key} ) {
      33        
1071 0         0 $self->{$key} =
1072             $block->( $key, $self->{$key}, $other_hash->{$key} );
1073             }
1074             else {
1075 2         11 $self->{$key} = $val;
1076             }
1077             }
1078              
1079 1         3 return $self;
1080             }
1081              
1082             *updateEx = \&mergeEx;
1083              
1084             =item min()
1085             Find the min element of a Ruby::Collections::Hash. If block is not given,
1086             transform each element to scalar then compare it.
1087            
1088             rh( 6 => 5, 11 => 3, 2 => 1 )->min # return [ 11, 3 ]
1089             rh( 6 => 5, 11 => 3, 2 => 1 )->min( sub {
1090             @{$_[0]}[1] - @{$_[0]}[0] <=> @{$_[1]}[1] - @{$_[1]}[0]
1091             })
1092             # return [ 11, 3 ]
1093             =cut
1094              
1095             sub min {
1096 2     2 1 5 my ( $self, $block ) = @_;
1097 2 50       9 ref($self) eq __PACKAGE__ or die;
1098              
1099 2         9 return $self->to_a->min($block);
1100             }
1101              
1102             =item min_by()
1103             Transform all elements by the given block and then find the max.
1104             Return the element which is the origin of the max.
1105            
1106             rh( 6 => 5, 11 => 3, 2 => 20 )->min_by( sub { @{$_[0]}[0] + @{$_[0]}[1] } ) # return [ 6, 5 ]
1107             =cut
1108              
1109             sub min_by {
1110 1     1 1 2 my ( $self, $block ) = @_;
1111 1 50       5 ref($self) eq __PACKAGE__ or die;
1112              
1113 1         3 return $self->to_a->min_by($block);
1114             }
1115              
1116             =item minmax()
1117             Find the min & max elements of a Ruby::Collections::Hash. If block is not given,
1118             transform each element to scalar then compare it.
1119            
1120             rh( 1 => 10, 2 => 9, 3 => 8 )->minmax # return [ [ 1, 10 ], [ 3, 8] ]
1121             rh( 1 => 10, 2 => 9, 3 => 8 )->minmax( sub {
1122             @{$_[0]}[1] - @{$_[0]}[0] <=> @{$_[1]}[1] - @{$_[1]}[0]
1123             })
1124             # return [ [ 3, 8 ], [ 1, 10 ] ]
1125             =cut
1126              
1127             sub minmax {
1128 2     2 1 6 my ( $self, $block ) = @_;
1129 2 50       9 ref($self) eq __PACKAGE__ or die;
1130              
1131 2         9 return $self->to_a->minmax($block);
1132             }
1133              
1134             =item minmax_by()
1135             Transform all elements by the given block and then find the min & max.
1136             Return the element which is the origin of the min & max.
1137            
1138             rh( 6 => 5, 11 => 3, 2 => 20 )->minmax_by( sub { @{$_[0]}[0] * @{$_[0]}[1] } )
1139             # return [ [ 6, 5 ], [ 2, 20 ] ]
1140             =cut
1141              
1142             sub minmax_by {
1143 1     1 1 3 my ( $self, $block ) = @_;
1144 1 50       5 ref($self) eq __PACKAGE__ or die;
1145              
1146 1         53 return $self->to_a->minmax_by($block);
1147             }
1148              
1149             =item has_none()
1150             If hash is empty, return 1, otherwise 0. If block is given and all results of block
1151             are false, return 1, otherwise 0.
1152            
1153             rh->has_none # return 1
1154             rh( 1 => 2 )->has_none # return 0
1155             rh( 'a' => 'b' )->has_none( sub {
1156             my ( $key, $val ) = @_;
1157             looks_like_number($key);
1158             })
1159             # return 1
1160             =cut
1161              
1162             sub has_none {
1163 3     3 1 6 my ( $self, $block ) = @_;
1164 3 50       13 ref($self) eq __PACKAGE__ or die;
1165              
1166 3 100       9 if ( defined $block ) {
1167 1         5 while ( my ( $key, $val ) = each %$self ) {
1168 1 50       6 return 0 if ( $block->( $key, $val ) );
1169             }
1170             }
1171             else {
1172 2         8 while ( my ( $key, $val ) = each %$self ) {
1173 1         6 return 0;
1174             }
1175             }
1176              
1177 2         13 return 1;
1178             }
1179              
1180             =item has_one()
1181             If hash has one element, return 1, otherwise 0. If block is given and one result of block
1182             are true, return 1, otherwise 0.
1183            
1184             rh->has_one # return 0
1185             rh( 1 => 2 )->has_one # return 1
1186             rh( 'a' => 'b', 1 => 2 )->has_one( sub {
1187             my ( $key, $val ) = @_;
1188             looks_like_number($key);
1189             })
1190             # return 1
1191             =cut
1192              
1193             sub has_one {
1194 3     3 1 6 my ( $self, $block ) = @_;
1195 3 50       10 ref($self) eq __PACKAGE__ or die;
1196              
1197 3         4 my $count = 0;
1198 3 100       8 if ( defined $block ) {
1199 1         5 while ( my ( $key, $val ) = each %$self ) {
1200 2 100       6 if ( $block->( $key, $val ) ) {
1201 1         6 $count++;
1202 1 50       8 return 0 if ( $count > 1 );
1203             }
1204             }
1205             }
1206             else {
1207 2         8 while ( my ( $key, $val ) = each %$self ) {
1208 1         2 $count++;
1209 1 50       9 return 0 if ( $count > 1 );
1210             }
1211             }
1212              
1213 3 100       21 return $count == 1 ? 1 : 0;
1214             }
1215              
1216             =item partition()
1217             Separate elements into 2 groups by given block.
1218            
1219             rh( 'a' => 1, 2 => 'b', 'c' => 3, 4 => 'd' )->partition( sub{
1220             my ( $key, $val ) = @_;
1221             looks_like_number($key);
1222             })
1223             =cut
1224              
1225             sub partition {
1226 1     1 1 4 my ( $self, $block ) = @_;
1227 1 50       6 ref($self) eq __PACKAGE__ or die;
1228              
1229 1         5 my $new_ary = ra;
1230 1         4 my $true_ary = ra;
1231 1         4 my $false_ary = ra;
1232 1         4 while ( my ( $key, $val ) = each %$self ) {
1233 4 100       12 if ( $block->( $key, $val ) ) {
1234 2         13 $true_ary->push( ra( $key, $val ) );
1235             }
1236             else {
1237 2         58 $false_ary->push( ra( $key, $val ) );
1238             }
1239             }
1240 1         4 $new_ary->push( $true_ary, $false_ary );
1241              
1242 1         9 return $new_ary;
1243             }
1244              
1245             =item rassoc()
1246             Find the value and return the key-value pair in a Ruby::Collections::Array.
1247             Return undef if value is not found.
1248            
1249             rh( 'a' => 123, 'b' => 123 )->rassoc(123) # return [ 'a', 123 ]
1250             rh( 'a' => 123, 'b' => 123 )->rassoc(456) # return undef
1251             =cut
1252              
1253             sub rassoc {
1254 2     2 1 5 my ( $self, $obj ) = @_;
1255 2 50       6 ref($self) eq __PACKAGE__ or die;
1256              
1257 2         6 while ( my ( $key, $val ) = each %$self ) {
1258 3 100       14 if ( $obj eq $val ) {
1259 1         14 return ra( $key, $val );
1260             }
1261             }
1262              
1263 1         11 return undef;
1264             }
1265              
1266             =item reject()
1267             Pass all key-value pairs into the block and store them into a Ruby::Collecitons::Array
1268             if the results returned by the block are false.
1269            
1270             rh( 1 => 3, 2 => 4, 5 => 6 )->reject( sub {
1271             my ( $key, $val ) = @_;
1272             $key % 2 == 1;
1273             } )
1274             # return { 2 => 4, 5 => 6 }
1275             =cut
1276              
1277             sub reject {
1278 1     1 1 3 my ( $self, $block ) = @_;
1279 1 50       6 ref($self) eq __PACKAGE__ or die;
1280              
1281 1         4 my $new_hash = rh($self);
1282 1         5 while ( my ( $key, $val ) = each %$new_hash ) {
1283 2 50       6 if ( $block->( $key, $val ) ) {
1284 2         22 delete $new_hash->{$key};
1285             }
1286             }
1287              
1288 1         6 return $new_hash;
1289             }
1290              
1291             =item rejectEx()
1292             Pass all key-value pairs into the block and remove them out of self
1293             if the results returned by the block are true. Return undef if nothing is deleted.
1294            
1295             rh( 1 => 3, 2 => 4 )->rejectEx( sub {
1296             my ( $key, $val ) = @_;
1297             $key % 2 == 1;
1298             } )
1299             # return { 2 => 4 }
1300             rh( 1 => 3, 2 => 4 )->rejectEx( sub {
1301             my ( $key, $val ) = @_;
1302             $key == 5;
1303             } )
1304             # return undef
1305             =cut
1306              
1307             sub rejectEx {
1308 2     2 1 6 my ( $self, $block ) = @_;
1309 2 50       7 ref($self) eq __PACKAGE__ or die;
1310              
1311 2         9 my $before_len = $self->size;
1312 2         11 $self->delete_if($block);
1313              
1314 2 100       8 if ( $self->size == $before_len ) {
1315 1         41 return undef;
1316             }
1317             else {
1318 1         6 return $self;
1319             }
1320             }
1321              
1322             =item reverse_each()
1323             Iterate key-value pair backward to a block.
1324            
1325             rh( 1 => 2, 3 => 4, 5 => 6 )->reverse_each( sub {
1326             my ( $key, $val ) = @_;
1327             print "$key, $val, ";
1328             } )
1329             # print "5, 6, 3, 4, 1, 2, "
1330             =cut
1331              
1332             sub reverse_each {
1333 1     1 1 4 my ( $self, $block ) = @_;
1334 1 50       6 ref($self) eq __PACKAGE__ or die;
1335              
1336 1         6 my $new_ary = $self->to_a->reverseEx;
1337 1 50       5 if ( defined $block ) {
1338 1         10 for my $item ( @{$new_ary} ) {
  1         2  
1339 3         69 $block->( @{$item}[0], @{$item}[1] );
  3         6  
  3         16  
1340             }
1341             }
1342              
1343 1         16 return $new_ary;
1344             }
1345              
1346             =item replace()
1347             Replace all elements with other hash.
1348            
1349             rh( 1 => 2 )->replace( { 3 => 4, 5 => 6 } ) # return { 3 => 4, 5 => 6 }
1350             =cut
1351              
1352             sub replace {
1353 1     1 1 9 my ( $self, $other_hash ) = @_;
1354 1 50       6 ref($self) eq __PACKAGE__ or die;
1355              
1356 1         6 %$self = %$other_hash;
1357              
1358 1         5 return $self;
1359             }
1360              
1361             =item select()
1362             Pass each key-value pair to the block and remain all elements
1363             which are true returned by the block in self. Return undef if
1364             nothing changed.
1365            
1366             rh( 'a' => 'b', 1 => 2, 'c' => 'd', 3 => '4')->select(
1367             sub {
1368             my ( $key, $val ) = @_;
1369             looks_like_number($key) && looks_like_number($val);
1370             }
1371             )
1372             # return { 1 => 2, 3 => 4 }
1373             =cut
1374              
1375             sub select {
1376 1     1 1 2 my ( $self, $block ) = @_;
1377 1 50       6 ref($self) eq __PACKAGE__ or die;
1378              
1379 1         4 my $new_hash = rh;
1380 1         5 while ( my ( $key, $val ) = each %$self ) {
1381 4 100       10 if ( $block->( $key, $val ) ) {
1382 2         19 $new_hash->{$key} = $val;
1383             }
1384             }
1385              
1386 1         6 return $new_hash;
1387             }
1388              
1389             =item selectEx()
1390             Pass each key-value pair to the block and remain all elements
1391             which are true returned by the block in self. Return undef if
1392             nothing changed.
1393            
1394             rh( 'a' => 'b', 1 => 2, 'c' => 'd', 3 => '4')->selectEx(
1395             sub {
1396             my ( $key, $val ) = @_;
1397             looks_like_number($key) && looks_like_number($val);
1398             }
1399             )
1400             # return { 1 => 2, 3 => 4 }
1401             rh( 'a' => 'b', 1 => 2, 'c' => 'd', 3 => '4')->selectEx(
1402             sub {
1403             my ( $key, $val ) = @_;
1404             $key == 5;
1405             }
1406             )
1407             # return undef
1408             =cut
1409              
1410             sub selectEx {
1411 2     2 1 19 my ( $self, $block ) = @_;
1412 2 50       9 ref($self) eq __PACKAGE__ or die;
1413              
1414 2         6 my $new_hash = rh;
1415 2         9 while ( my ( $key, $val ) = each %$self ) {
1416 8 100       23 if ( $block->( $key, $val ) ) {
1417 6         49 $new_hash->{$key} = $val;
1418             }
1419             }
1420              
1421 2 100       13 if ( $new_hash->size == $self->size ) {
1422 1         10 return undef;
1423             }
1424             else {
1425 1         6 %$self = %$new_hash;
1426 1         10 return $self;
1427             }
1428             }
1429              
1430             =item shift()
1431             Shift the first key-value pair out of self.
1432            
1433             rh( 1 => 2 )->shift # return [ 1, 2 ]
1434             rh->shift # undef
1435             =cut
1436              
1437             sub shift {
1438 2     2 1 9 my ($self) = @_;
1439 2 50       8 ref($self) eq __PACKAGE__ or die;
1440              
1441 2         9 while ( my ( $key, $val ) = each %$self ) {
1442 1         5 my $new_ary = ra( $key, $val );
1443 1         7 delete $self->{$key};
1444 1         8 return $new_ary;
1445             }
1446              
1447 1         5 return undef;
1448             }
1449              
1450             =item slice_before()
1451             Separate elements into groups, the first element of each group is
1452             defined by block or regex.
1453            
1454             rh( 'a' => 1, 'b' => 0, 'c' => 0, 'd' => 1 )->slice_before( sub {
1455             my ( $key, $val ) = @_;
1456             $val == 0;
1457             } )
1458             # return [ [ [ a, 1 ] ], [ [ b, 0 ] ], [ [ c, 0 ], [ d, 1 ] ] ]
1459             rh( 'a' => 1, 'b' => 0, 'c' => 0, 'd' => 1 )->slice_before(qr/^\[[a-z]/)
1460             # return [ [ [ a, 1 ] ], [ [ b, 0 ] ], [ [ c, 0 ] ], [ [ d, 1 ] ] ]
1461             =cut
1462              
1463             sub slice_before {
1464 2     2 1 181 my $self = shift @_;
1465 2 50       8 ref($self) eq __PACKAGE__ or die;
1466              
1467 2         9 my $new_ary = tie my @new_ary, 'Ruby::Collections::Array';
1468 2         11 my $group = undef;
1469 2 100       44 if ( ref( @_[0] ) eq 'CODE' ) {
1470 1         2 my $block = shift @_;
1471              
1472 1         5 while ( my ( $key, $val ) = each %$self ) {
1473 4 100       13 if ( not defined $group ) {
    100          
1474 1         4 $group = tie my @group, 'Ruby::Collections::Array';
1475 1         7 push( @group, ra( $key, $val ) );
1476             }
1477             elsif ( $block->( $key, $val ) ) {
1478 2         14 push( @new_ary, $group );
1479 2         12 $group = tie my @group, 'Ruby::Collections::Array';
1480 2         12 push( @group, ra( $key, $val ) );
1481             }
1482             else {
1483 1         5 push( @{$group}, ra( $key, $val ) );
  1         3  
1484             }
1485             }
1486             }
1487             else {
1488 1         3 my $pattern = shift @_;
1489              
1490 1         6 while ( my ( $key, $val ) = each %$self ) {
1491 4 100       16 if ( not defined $group ) {
    50          
1492 1         4 $group = tie my @group, 'Ruby::Collections::Array';
1493 1         9 push( @group, ra( $key, $val ) );
1494             }
1495             elsif ( ra( $key, $val )->to_s =~ $pattern ) {
1496 3         9 push( @new_ary, $group );
1497 3         20 $group = tie my @group, 'Ruby::Collections::Array';
1498 3         17 push( @group, ra( $key, $val ) );
1499             }
1500             else {
1501 0         0 push( @{$group}, ra( $key, $val ) );
  0         0  
1502             }
1503             }
1504             }
1505 2 50 33     17 if ( defined $group && $group->has_any ) {
1506 2         6 push( @new_ary, $group );
1507             }
1508              
1509 2         27 return $new_ary;
1510             }
1511              
1512             =item store()
1513             Store a key-value pair.
1514            
1515             rh( 1 => 2 )->store( 3, 4 ) # return 4
1516             =cut
1517              
1518             sub store {
1519 1     1 1 7 my ( $self, $key, $val ) = @_;
1520 1 50       7 ref($self) eq __PACKAGE__ or die;
1521              
1522 1         6 $self->{$key} = $val;
1523              
1524 1         7 return $val;
1525             }
1526              
1527             =item take()
1528             Take first n elements and put them into a Ruby::Collections::Array.
1529            
1530             rh( 1 => 2, 3 => 4, 5 => 6 )->take(2) # return [ [ 1, 2 ], [ 3, 4 ] ]
1531             =cut
1532              
1533             sub take {
1534 2     2 1 6 my ( $self, $n ) = @_;
1535 2 50       7 ref($self) eq __PACKAGE__ or die;
1536              
1537 2 50       5 if ( defined $n ) {
1538 2 100       20 die 'ArgumentError: negative array size' if ( $n < 0 );
1539              
1540 1         5 my $new_ary = ra;
1541 1         6 while ( my ( $key, $val ) = each %$self ) {
1542 3 100       9 if ( $n <= 0 ) {
1543 1         8 return $new_ary;
1544             }
1545 2         6 $new_ary->push( ra( $key, $val ) );
1546 2         12 $n--;
1547             }
1548 0         0 return $new_ary;
1549             }
1550             else {
1551 0         0 die 'ArgumentError: wrong number of arguments (0 for 1)';
1552             }
1553             }
1554              
1555             =item take_while()
1556             Start to take elements while result returned by block is true and
1557             put them into a Ruby::Collections::Array.
1558            
1559             rh( 1 => 2, 3 => 4, 5 => 6 )->take_while( sub {
1560             my ( $key, $val ) = @_;
1561             $key <= 3;
1562             } )
1563             # return [ [ 1, 2 ], [ 3, 4 ] ]
1564             =cut
1565              
1566             sub take_while {
1567 1     1 1 2 my ( $self, $block ) = @_;
1568 1 50       9 ref($self) eq __PACKAGE__ or die;
1569              
1570 1         4 my $new_ary = ra;
1571 1         6 while ( my ( $key, $val ) = each %$self ) {
1572 3 100       9 if ( $block->( $key, $val ) ) {
1573 2         18 $new_ary->push( ra( $key, $val ) );
1574             }
1575             else {
1576 1         10 return $new_ary;
1577             }
1578             }
1579              
1580 0         0 return $new_ary;
1581             }
1582              
1583             =item to_a()
1584             Converts self to a nested array of [ key, value ] Ruby::Collections::Array.
1585            
1586             rh( 1 => 2, 'a' => 'b' )->to_a # return [ [ 1, 2 ], [ a, b ] ]
1587             =cut
1588              
1589             sub to_a {
1590 22     22 1 48 my ($self) = @_;
1591 22 50       73 ref($self) eq __PACKAGE__ or die;
1592              
1593 22         63 my $new_array = ra();
1594 22         98 while ( my ( $key, $val ) = each %$self ) {
1595 64         154 $new_array->push( ra( $key, $val ) );
1596             }
1597              
1598 22         139 return $new_array;
1599             }
1600              
1601             =item to_h()
1602             Return self;
1603             Alias: to_hash()
1604             =cut
1605              
1606             sub to_h {
1607 1     1 1 7 my ($self) = @_;
1608 1 50       6 ref($self) eq __PACKAGE__ or die;
1609              
1610 1         16 return $self;
1611             }
1612              
1613             *to_hash = \&to_h;
1614              
1615             =item has_value()
1616             Retuen 1 if a value exists, otherwise 0.
1617            
1618             rh( 1 => 2, 3 => 4 )->has_value(4) # return 1
1619             rh( 1 => 2, 3 => 4 )->has_value(5) # return 0
1620             =cut
1621              
1622             sub has_value {
1623 2     2 1 4 my ( $self, $val ) = @_;
1624 2 50       8 ref($self) eq __PACKAGE__ or die;
1625              
1626 2         11 return ra( values %$self )->include($val);
1627             }
1628              
1629             =item values_at()
1630             Put all values corresponding to the input keys into a Ruby::Collections::Array.
1631            
1632             rh( 1 => 2, 3 => 4, 5 => 6 )->values_at( 3, 4, 6 ) # return [ 4, undef, undef ]
1633             =cut
1634              
1635             sub values_at {
1636 1     1 1 3 my $self = shift @_;
1637 1 50       6 ref($self) eq __PACKAGE__ or die;
1638              
1639 1         5 my $new_array = ra();
1640 1         5 for my $key (@_) {
1641 3         18 $new_array->push( $self->{$key} );
1642             }
1643              
1644 1         6 return $new_array;
1645             }
1646              
1647             =item zip()
1648             Call to_a first, then zip an array of elements into self.
1649            
1650             rh( 1 => [ 2, 3 ], 4 => [ 5, 6 ], 7 => 8 )->zip( [ 9, 10 ] )
1651             # return [ [ [ 1, [ 2, 3 ] ], 9 ], [ [ 4, [ 5, 6 ] ], 10 ], [ [ 7, 8 ], undef ] ]
1652             =cut
1653              
1654             sub zip {
1655 1     1 1 3 my $self = shift @_;
1656 1 50       6 ref($self) eq __PACKAGE__ or die;
1657              
1658 1         5 return $self->to_a->zip(@_);
1659             }
1660              
1661             if ( __FILE__ eq $0 ) {
1662             rh( 1 => 2, 3 => 4 )->each_entry( sub{
1663             my ( $k, $v ) = @_;
1664             } );
1665             }
1666              
1667             1;
1668             __END__;