File Coverage

blib/lib/Array/Pick/Scan.pm
Criterion Covered Total %
statement 37 47 78.7
branch 14 26 53.8
condition 3 4 75.0
subroutine 6 6 100.0
pod 1 1 100.0
total 61 84 72.6


line stmt bran cond sub pod time code
1             package Array::Pick::Scan;
2              
3 1     1   56062 use 5.010001;
  1         11  
4 1     1   4 use strict;
  1         2  
  1         15  
5 1     1   4 use warnings;
  1         1  
  1         30  
6              
7 1     1   5 use Exporter qw(import);
  1         1  
  1         358  
8              
9             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
10             our $DATE = '2022-05-21'; # DATE
11             our $DIST = 'Array-Pick-Scan'; # DIST
12             our $VERSION = '0.005'; # VERSION
13              
14             our @EXPORT_OK = qw(random_item pick);
15              
16             sub random_item {
17 203     203 1 4485 my ($src, $num_items, $opts) = @_;
18 203         238 my $ref = ref $src;
19              
20 203   50     284 $num_items //= 1;
21 203   100     527 $opts //= {};
22              
23 203 100       314 if ($ref eq 'ARRAY') {
    50          
24 103         103 my $ary = $src;
25 103         107 my $ary_size = @$ary;
26              
27 103 100       154 if (!$ary_size) {
    50          
28 2         11 return ();
29             } elsif ($num_items == 1) {
30 101         172 my $idx = int(rand() * $ary_size);
31 101 100       253 return $opts->{pos} ? $idx : $ary->[$idx];
32             } else {
33 0         0 my @items;
34 0         0 for my $i (0..$ary_size-1) {
35 0 0       0 if (@items < $num_items) {
36             # we haven't reached $num_items, insert item to array in a
37             # random position
38 0         0 my $idx = int(rand(@items+1));
39 0 0       0 splice @items, $idx, 0, ($opts->{pos} ? $i : $ary->[$i]);
40             } else {
41             # we have reached $num_items, just replace an item randomly,
42             # using algorithm from Learning Perl, slightly modified
43 0 0       0 if (rand($i+1) < @items) {
44 0         0 my $idx = int(rand(@items));
45 0 0       0 splice @items, $idx, 1, ($opts->{pos} ? $i : $ary->[$i]);
46             }
47             }
48             }
49 0         0 return @items;
50             }
51             } elsif ($ref eq 'CODE') {
52 100         102 my $iter = $src;
53 100         118 my @items;
54 100         100 my $i = -1;
55 100         132 while (defined(my $item = $iter->())) {
56 1000         3526 $i++;
57 1000 100       1262 if (@items < $num_items) {
58             # we haven't reached $num_items, insert item to array in a
59             # random position
60 100         147 my $idx = int(rand(@items+1));
61 100 50       210 splice @items, $idx, 0, ($opts->{pos} ? $i : $item);
62             } else {
63             # we have reached $num_items, just replace an item randomly,
64             # using algorithm from Learning Perl, slightly modified
65 900 100       1775 if (rand($i+1) < @items) {
66 200         245 my $idx = int(rand(@items));
67 200 50       417 splice @items, $idx, 1, $opts->{pos} ? $i : $item;
68             }
69             }
70             }
71 100         540 return @items;
72             } else {
73 0           die "Please specify arrayref or coderef iterator as source of items";
74             }
75             }
76              
77             {
78 1     1   7 no strict 'refs'; ## no critic: TestingAndDebugging::ProhibitNoStrict
  1         1  
  1         60  
79             *pick = \&random_item;
80             }
81              
82             1;
83             # ABSTRACT: Pick random items from an array (or iterator), without duplicates
84              
85             __END__