File Coverage

blib/lib/SHARYANTO/Array/Util.pm
Criterion Covered Total %
statement 51 51 100.0
branch 17 18 94.4
condition 5 6 83.3
subroutine 9 9 100.0
pod 3 3 100.0
total 85 87 97.7


line stmt bran cond sub pod time code
1             package SHARYANTO::Array::Util;
2              
3             our $DATE = '2015-09-04'; # DATE
4             our $VERSION = '0.77'; # VERSION
5              
6 1     1   19674 use 5.010;
  1         4  
7 1     1   4 use strict;
  1         1  
  1         19  
8 1     1   11 use warnings;
  1         2  
  1         28  
9 1     1   737 use experimental 'smartmatch';
  1         3750  
  1         5  
10              
11 1     1   1030 use Perinci::Sub::Util qw(gen_modified_sub);
  1         2516  
  1         364  
12              
13             require Exporter;
14             our @ISA = qw(Exporter);
15             our @EXPORT_OK = qw(
16             match_array_or_regex
17             match_regex_or_array
18             split_array
19             replace_array_content
20             );
21              
22             our %SPEC;
23              
24             $SPEC{':package'} = {
25             v => 1.1,
26             summary => 'Array-related utilities',
27             };
28              
29             my $_str_or_re = ['any*'=>{of=>['re*','str*']}];
30              
31             $SPEC{match_array_or_regex} = {
32             v => 1.1,
33             summary => 'Check whether an item matches (list of) values/regexes',
34             description => <<'_',
35              
36             This routine can be used to match an item against a regex or a list of
37             strings/regexes, e.g. when matching against an ACL.
38              
39             Since the smartmatch (`~~`) operator can already match against a list of strings
40             or regexes, this function is currently basically equivalent to:
41              
42             if (ref($haystack) eq 'ARRAY') {
43             return $needle ~~ @$haystack;
44             } else {
45             return $needle =~ /$haystack/;
46             }
47              
48             _
49             examples => [
50             {args=>{needle=>"abc", haystack=>["abc", "abd"]}, result=>1},
51             {args=>{needle=>"abc", haystack=>qr/ab./}, result=>1},
52             {args=>{needle=>"abc", haystack=>[qr/ab./, "abd"]}, result=>1},
53             ],
54             args_as => 'array',
55             args => {
56             needle => {
57             schema => ["str*"],
58             pos => 0,
59             req => 1,
60             },
61             haystack => {
62             # XXX checking this schema might actually take longer than matching
63             # the needle! so when arg validation is implemented, provide a way
64             # to skip validating this schema
65              
66             schema => ["any*" => {
67             of => [$_str_or_re, ["array*"=>{of=>$_str_or_re}]],
68             }],
69             pos => 1,
70             req => 1,
71             },
72             },
73             result_naked => 1,
74             };
75             sub match_array_or_regex {
76 11     11 1 365 my ($needle, $haystack) = @_;
77 11         18 my $ref = ref($haystack);
78 11 100       35 if ($ref eq 'ARRAY') {
    100          
    100          
79 5         37 return $needle ~~ @$haystack;
80             } elsif (!$ref) {
81 1         16 return $needle =~ /$haystack/;
82             } elsif ($ref eq 'Regexp') {
83 4         31 return $needle =~ $haystack;
84             } else {
85 1         11 die "Invalid haystack, must be regex or array of strings/regexes";
86             }
87             }
88              
89             gen_modified_sub(
90             output_name => 'match_regex_or_array',
91             base_name => 'match_array_or_regex',
92             summary => 'Alias for match_array_or_regex',
93             );
94              
95             sub split_array {
96 1     1   7 no strict 'refs';
  1         2  
  1         349  
97              
98 6     6 1 651 my ($pat, $ary, $limit) = @_;
99              
100 6 50       17 die "BUG: Second argument must be an array" unless ref($ary) eq 'ARRAY';
101 6 100       30 $pat = qr/\A\Q$pat\E\z/ unless ref($pat) eq 'Regexp';
102              
103 6         7 my @res;
104 6         7 my $num_elems = 0;
105 6         6 my $i = 0;
106             ELEM:
107 6         16 while ($i < @$ary) {
108 15         20 push @res, [];
109             COLLECT:
110 15         17 while (1) {
111 40 100       151 if ($ary->[$i] =~ $pat) {
112 10 100       82 push @res, [map { ${"$_"} } 1..@+-1] if @+ > 1;
  6         6  
  6         25  
113 10         15 last COLLECT;
114             }
115 30         28 push @{ $res[-1] }, $ary->[$i];
  30         50  
116 30 100       79 last ELEM unless ++$i < @$ary;
117             }
118 10         9 $num_elems++;
119             LIMIT:
120 10 100 66     33 if (defined($limit) && $limit > 0 && $num_elems >= $limit) {
      100        
121 1         4 push @{ $res[-1] }, $ary->[$_] for $i..(@$ary-1);
  2         5  
122 1         2 last ELEM;
123             }
124 9         16 $i++;
125             }
126              
127 6         47 return @res;
128             }
129              
130             sub replace_array_content {
131 1     1 1 487 my $aryref = shift;
132 1         3 @$aryref = @_;
133 1         3 $aryref;
134             }
135              
136             1;
137             # ABSTRACT: Array-related utilities
138              
139             __END__
140              
141             =pod
142              
143             =encoding UTF-8
144              
145             =head1 NAME
146              
147             SHARYANTO::Array::Util - Array-related utilities
148              
149             =head1 VERSION
150              
151             This document describes version 0.77 of SHARYANTO::Array::Util (from Perl distribution SHARYANTO-Utils), released on 2015-09-04.
152              
153             =head1 SYNOPSIS
154              
155             use SHARYANTO::Array::Util qw(match_array_or_regex split_array);
156              
157             match_array_or_regex('bar', ['foo', 'bar', qr/[xyz]/]); # true, matches string
158             match_array_or_regex('baz', ['foo', 'bar', qr/[xyz]/]); # true, matches regex
159             match_array_or_regex('oops', ['foo', 'bar', qr/[xyz]/]); # false
160              
161             my @res = split_array('--', [qw/--opt1 --opt2 -- foo bar -- --val/]);
162             # -> ([qw/--opt1 --opt2/], [qw/foo bar/], [qw/--val/])
163              
164             my @res = split_array(qr/--/, [qw/--opt1 --opt2 -- foo bar -- --val/], 2);
165             # -> ([qw/--opt1 --opt2/], [qw/foo bar -- --val/])
166              
167             my @res = split_array(qr/(--)/, [qw/--opt1 --opt2 -- foo bar -- --val/], 2);
168             # -> ([qw/--opt1 --opt2/], [qw/--/], [qw/foo bar -- --val/])
169              
170             my @res = split_array(qr/(-)(-)/, [qw/--opt1 --opt2 -- foo bar -- --val/], 2);
171             # -> ([qw/--opt1 --opt2/], [qw/- -/], [qw/foo bar -- --val/])
172              
173             =head1 DESCRIPTION
174              
175             =head1 FUNCTIONS
176              
177              
178             =head2 match_array_or_regex($needle, $haystack) -> any
179              
180             Check whether an item matches (list of) values/regexes.
181              
182             Examples:
183              
184             match_array_or_regex("abc", ["abc", "abd"]); # -> 1
185             match_array_or_regex("abc", qr/ab./); # -> 1
186             match_array_or_regex("abc", [qr/ab./, "abd"]); # -> 1
187             This routine can be used to match an item against a regex or a list of
188             strings/regexes, e.g. when matching against an ACL.
189              
190             Since the smartmatch (C<~~>) operator can already match against a list of strings
191             or regexes, this function is currently basically equivalent to:
192              
193             if (ref($haystack) eq 'ARRAY') {
194             return $needle ~~ @$haystack;
195             } else {
196             return $needle =~ /$haystack/;
197             }
198              
199             Arguments ('*' denotes required arguments):
200              
201             =over 4
202              
203             =item * B<haystack>* => I<re|str|array[re|str]>
204              
205             =item * B<needle>* => I<str>
206              
207             =back
208              
209             Return value: (any)
210              
211              
212             =head2 match_regex_or_array($needle, $haystack) -> any
213              
214             Alias for match_array_or_regex.
215              
216             Examples:
217              
218             match_regex_or_array("abc", ["abc", "abd"]); # -> 1
219             match_regex_or_array("abc", qr/ab./); # -> 1
220             match_regex_or_array("abc", [qr/ab./, "abd"]); # -> 1
221             This routine can be used to match an item against a regex or a list of
222             strings/regexes, e.g. when matching against an ACL.
223              
224             Since the smartmatch (C<~~>) operator can already match against a list of strings
225             or regexes, this function is currently basically equivalent to:
226              
227             if (ref($haystack) eq 'ARRAY') {
228             return $needle ~~ @$haystack;
229             } else {
230             return $needle =~ /$haystack/;
231             }
232              
233             Arguments ('*' denotes required arguments):
234              
235             =over 4
236              
237             =item * B<haystack>* => I<re|str|array[re|str]>
238              
239             =item * B<needle>* => I<str>
240              
241             =back
242              
243             Return value: (any)
244              
245             =head2 split_array($str_or_re, \@array[, $limit]) => LIST
246              
247             Like the C<split()> builtin Perl function, but applies on an array instead of a
248             scalar. It loosely follows the C<split()> semantic, with some exceptions.
249              
250             =head2 replace_array_content($aryref, @elems) => $aryref
251              
252             Replace elements in <$aryref> with @elems. Return C<$aryref>. Do not create a
253             new arrayref object (i.e. it is different from: C<< $aryref = ["new", "content"]
254             >>).
255              
256             Do not use this function. In Perl you can just use: C<< splice(@$aryref, 0,
257             length(@$aryref), @elems) >> or even easier: C<< @$aryref = @elems >>. I put the
258             function here for reminder.
259              
260             =head1 SEE ALSO
261              
262             L<SHARYANTO>
263              
264             =head1 HOMEPAGE
265              
266             Please visit the project's homepage at L<https://metacpan.org/release/SHARYANTO-Utils>.
267              
268             =head1 SOURCE
269              
270             Source repository is at L<https://github.com/perlancar/perl-SHARYANTO-Utils>.
271              
272             =head1 BUGS
273              
274             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=SHARYANTO-Utils>
275              
276             When submitting a bug or request, please include a test-file or a
277             patch to an existing test-file that illustrates the bug or desired
278             feature.
279              
280             =head1 AUTHOR
281              
282             perlancar <perlancar@cpan.org>
283              
284             =head1 COPYRIGHT AND LICENSE
285              
286             This software is copyright (c) 2015 by perlancar@cpan.org.
287              
288             This is free software; you can redistribute it and/or modify it under
289             the same terms as the Perl 5 programming language system itself.
290              
291             =cut