File Coverage

blib/lib/List/NSect.pm
Criterion Covered Total %
statement 36 36 100.0
branch 16 16 100.0
condition 9 9 100.0
subroutine 10 10 100.0
pod 3 3 100.0
total 74 74 100.0


line stmt bran cond sub pod time code
1             package List::NSect;
2 6     6   408409 use strict;
  6         68  
  6         175  
3 6     6   60 use warnings;
  6         14  
  6         237  
4 6     6   54 use base qw{Exporter};
  6         10  
  6         979  
5 6     6   3623 use List::MoreUtils qw{part};
  6         83181  
  6         38  
6              
7             our $VERSION = '0.07';
8             our @EXPORT = qw(nsect);
9             our @EXPORT_OK = qw(spart deal);
10              
11             =head1 NAME
12              
13             List::NSect - Cuts or divides a list into N equal parts.
14              
15             =head1 SYNOPSIS
16              
17             use List::NSect;
18             my @sections=nsect(5 => "a" .. "z");
19             foreach my $section (@sections) {
20             print join(",", @$section), "\n";
21             }
22              
23             Output
24              
25             a,b,c,d,e,f
26             g,h,i,j,k
27             l,m,n,o,p
28             q,r,s,t,u
29             v,w,x,y,z
30              
31             =head1 DESCRIPTION
32              
33             List::NSect is an L that exports the function "nsect".
34              
35             =head2 nsect like bisect not mosquito
36              
37             I had a hard time deciding on a function name that was distinct and succinct. When I searched the Internet for "divide into equal parts", "bisect - to divide into two equal parts" was one of the top hits. I then tried to find a synonym for "divide into N equal parts". I soon realized that there is no single English word for the concept: thus "nsect".
38              
39             Other function names that I was contemplating are "chunk" (to cut, break, or form into chunks), "allot" (to divide or distribute by share or portion) and "apportion" (to distribute or allocate proportionally; divide and assign according to some rule of proportional distribution). None of these names implies the need for exactly N sections instead of some other distribution.
40              
41             I use this capability all of the time which is a specific implementation of List::MoreUtils::part. You may ask `why not just use "part" directly from L?` Well, there are many edge cases. Please, take a look at the code; This is Perl!
42              
43             =head1 USAGE
44              
45             use List::NSect;
46             my @sections=nsect($n => @list); #returns ([...], [...], [...], ...); #$n count of arrray references
47              
48             use List::NSect qw{spart};
49             my @batches=spart($n => @list); #returns ([...], [...], [...], ...); #array reference of $n size
50              
51             =head1 FUNCTION
52              
53             =head2 nsect
54              
55             Cuts or divides a list into N equal or nearly equal parts.
56              
57             Returns an array of array references given a scalar number of sections and a list.
58              
59             my @sections=nsect(4, 1 .. 17); #returns ([1,2,3,4,5],[6,7,8,9],[10,11,12,13],[14,15,16,17]);
60             my $sections=nsect(4, 1 .. 17); #returns [[1,2,3,4,5],[6,7,8,9],[10,11,12,13],[14,15,16,17]];
61              
62             =cut
63              
64             sub nsect {
65 28   100 28 1 47371 my $n = shift || 0;
66 28         51 my $count = scalar(@_);
67 28         63 my @sections = ();
68             #undef, 0 or empty array returns nothing as requested
69 28 100 100     103 if ($n > 0 and $count > 0) {
70 8 100       25 $n=$count if $n > $count;
71 8         13 my $i=0;
72 8     52   80 @sections=part {int($i++ * $n / $count)} @_; #Each partition created is a reference to an array.
  52         118  
73             }
74 28 100       115 return wantarray ? @sections : \@sections;
75             }
76              
77             =head2 spart (not exported by default)
78              
79             Cut or divides a list into parts each of size N.
80              
81             Returns an array of array references given a scalar size and a list.
82              
83             my @parts=spart(4, 1 .. 17); #returns ([1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16],[17]);
84             my $parts=spart(4, 1 .. 17); #returns [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16],[17]];
85              
86             Note: The last array reference may be short.
87              
88             =cut
89              
90             sub spart {
91 28   100 28 1 48437 my $parts = shift || 0;
92 28         54 my @deck = ();
93             #undef, 0 or empty array returns nothing as requested
94 28 100       66 if ($parts > 0) {
95 16         25 my $i = 0;
96 16     52   140 @deck = part {int($i++ / $parts)} @_; #/#Each partition created is a reference to an array.
  52         144  
97             }
98 28 100       135 return wantarray ? @deck : \@deck;
99             }
100              
101             =head2 deal (not exported by default)
102              
103             Deals a list into hands
104              
105             Returns an array of array references given a scalar size and a list.
106              
107             my @hands=deal(4, 1 .. 17); #returns ([1,5,9,13,17],[2,6,10,14],[3,7,11,15],[4,8,12,16]);
108              
109             =cut
110              
111             sub deal {
112 28   100 28 1 51839 my $hands = shift || 0;
113 28         52 my @deck = ();
114             #undef, 0 or empty array returns nothing as requested
115 28 100       69 if ($hands > 0) {
116 16         28 my $hand = 0;
117 16 100   52   112 @deck=part {$hand=0 if $hand>=$hands; $hand++;} @_;
  52         89  
  52         91  
118             }
119 28 100       135 return wantarray ? @deck : \@deck;
120             }
121              
122             =head1 LIMITATIONS
123              
124             my @sections=nsect($n => @list);
125              
126             The nsect function will ALWAYS return an array (array reference in scalar context). So, that you can always pass the return directly into a foreach loop without the need to test for edge cases. However, I made the executive decision that if $n > scalar(@list) the returned array, @sections, is not $n in size but rather scalar(@list) in size.
127              
128             my @sections=nsect(100, "a", "b", "c"); #scalar(@sections) == 3 != 100;
129              
130             =head1 BUGS
131              
132             Please open an issue on GitHub
133              
134             =head1 AUTHOR
135              
136             Michael R. Davis
137              
138             =head1 COPYRIGHT
139              
140             MIT License
141              
142             Copyright (c) 2022 Michael R. Davis
143              
144             =head1 SEE ALSO
145              
146             L part and natatime, L, L bundle_by, L, L, L
147              
148             =cut
149              
150             1;