add files
|
1 |
package Mojo::Collection; |
2 |
use Mojo::Base -strict; |
|
3 |
use overload bool => sub {1}, '""' => sub { shift->join("\n") }, fallback => 1; |
|
4 | ||
5 |
use Carp 'croak'; |
|
6 |
use Exporter 'import'; |
|
7 |
use List::Util; |
|
8 |
use Mojo::ByteStream; |
|
9 |
use Scalar::Util 'blessed'; |
|
10 | ||
11 |
our @EXPORT_OK = ('c'); |
|
12 | ||
13 |
sub AUTOLOAD { |
|
14 |
my $self = shift; |
|
15 | ||
16 |
my ($package, $method) = our $AUTOLOAD =~ /^([\w:]+)::(\w+)$/; |
|
17 |
croak "Undefined subroutine &${package}::$method called" |
|
18 |
unless blessed $self && $self->isa(__PACKAGE__); |
|
19 | ||
20 |
croak qq{Can't locate object method "$method" via package "$package"} |
|
21 |
unless @$self; |
|
22 |
return $self->pluck($method, @_); |
|
23 |
} |
|
24 | ||
25 |
sub DESTROY { } |
|
26 | ||
27 |
sub new { |
|
28 |
my $class = shift; |
|
29 |
return bless [@_], ref $class || $class; |
|
30 |
} |
|
31 | ||
32 |
sub c { __PACKAGE__->new(@_) } |
|
33 | ||
34 |
sub compact { |
|
35 |
shift->grep(sub { length($_ // '') }); |
|
36 |
} |
|
37 | ||
38 |
sub each { |
|
39 |
my ($self, $cb) = @_; |
|
40 |
return @$self unless $cb; |
|
41 |
my $i = 1; |
|
42 |
$_->$cb($i++) for @$self; |
|
43 |
return $self; |
|
44 |
} |
|
45 | ||
46 |
sub first { |
|
47 |
my ($self, $cb) = @_; |
|
48 |
return $self->[0] unless $cb; |
|
49 |
return List::Util::first { $cb->($_) } @$self if ref $cb eq 'CODE'; |
|
50 |
return List::Util::first { $_ =~ $cb } @$self; |
|
51 |
} |
|
52 | ||
53 |
sub flatten { $_[0]->new(_flatten(@{$_[0]})) } |
|
54 | ||
55 |
sub grep { |
|
56 |
my ($self, $cb) = @_; |
|
57 |
return $self->new(grep { $cb->($_) } @$self) if ref $cb eq 'CODE'; |
|
58 |
return $self->new(grep { $_ =~ $cb } @$self); |
|
59 |
} |
|
60 | ||
61 |
sub join { Mojo::ByteStream->new(join $_[1], map({"$_"} @{$_[0]})) } |
|
62 | ||
63 |
sub map { |
|
64 |
my ($self, $cb) = @_; |
|
65 |
return $self->new(map { $_->$cb } @$self); |
|
66 |
} |
|
67 | ||
68 |
sub pluck { |
|
69 |
my ($self, $method, @args) = @_; |
|
70 |
return $self->map(sub { $_->$method(@args) }); |
|
71 |
} |
|
72 | ||
73 |
sub reverse { $_[0]->new(reverse @{$_[0]}) } |
|
74 | ||
75 |
sub shuffle { $_[0]->new(List::Util::shuffle @{$_[0]}) } |
|
76 | ||
77 |
sub size { scalar @{$_[0]} } |
|
78 | ||
79 |
sub slice { |
|
80 |
my $self = shift; |
|
81 |
return $self->new(@$self[@_]); |
|
82 |
} |
|
83 | ||
84 |
sub sort { |
|
85 |
my ($self, $cb) = @_; |
|
86 |
return $self->new($cb ? sort { $a->$cb($b) } @$self : sort @$self); |
|
87 |
} |
|
88 | ||
89 |
sub tap { shift->Mojo::Base::tap(@_) } |
|
90 | ||
91 |
sub uniq { |
|
92 |
my $self = shift; |
|
93 |
my %seen; |
|
94 |
return $self->grep(sub { !$seen{$_}++ }); |
|
95 |
} |
|
96 | ||
97 |
sub _flatten { |
|
98 |
map { _ref($_) ? _flatten(@$_) : $_ } @_; |
|
99 |
} |
|
100 | ||
101 |
sub _ref { ref $_[0] && (ref $_[0] eq 'ARRAY' || $_[0]->isa(__PACKAGE__)) } |
|
102 | ||
103 |
1; |
|
104 | ||
105 |
=encoding utf8 |
|
106 | ||
107 |
=head1 NAME |
|
108 | ||
109 |
Mojo::Collection - Collection |
|
110 | ||
111 |
=head1 SYNOPSIS |
|
112 | ||
113 |
# Manipulate collections |
|
114 |
use Mojo::Collection; |
|
115 |
my $collection = Mojo::Collection->new(qw(just works)); |
|
116 |
unshift @$collection, 'it'; |
|
117 |
$collection->map(sub { ucfirst })->each(sub { |
|
118 |
my ($word, $count) = @_; |
|
119 |
say "$count: $word"; |
|
120 |
}); |
|
121 | ||
122 |
# Use the alternative constructor |
|
123 |
use Mojo::Collection 'c'; |
|
124 |
c(qw(a b c))->join('/')->url_escape->say; |
|
125 | ||
126 |
=head1 DESCRIPTION |
|
127 | ||
128 |
L<Mojo::Collection> is a container for collections. |
|
129 | ||
130 |
=head1 FUNCTIONS |
|
131 | ||
132 |
L<Mojo::Collection> implements the following functions. |
|
133 | ||
134 |
=head2 c |
|
135 | ||
136 |
my $collection = c(1, 2, 3); |
|
137 | ||
138 |
Construct a new array-based L<Mojo::Collection> object. |
|
139 | ||
140 |
=head1 METHODS |
|
141 | ||
142 |
L<Mojo::Collection> implements the following methods. |
|
143 | ||
144 |
=head2 new |
|
145 | ||
146 |
my $collection = Mojo::Collection->new(1, 2, 3); |
|
147 | ||
148 |
Construct a new array-based L<Mojo::Collection> object. |
|
149 | ||
150 |
=head2 compact |
|
151 | ||
152 |
my $new = $collection->compact; |
|
153 | ||
154 |
Create a new collection with all elements that are defined and not an empty |
|
155 |
string. |
|
156 | ||
157 |
=head2 each |
|
158 | ||
159 |
my @elements = $collection->each; |
|
160 |
$collection = $collection->each(sub {...}); |
|
161 | ||
162 |
Evaluate callback for each element in collection or return all elements as a |
|
163 |
list if none has been provided. The element will be the first argument passed |
|
164 |
to the callback and is also available as C<$_>. |
|
165 | ||
166 |
$collection->each(sub { |
|
167 |
my ($e, $count) = @_; |
|
168 |
say "$count: $e"; |
|
169 |
}); |
|
170 | ||
171 |
=head2 first |
|
172 | ||
173 |
my $first = $collection->first; |
|
174 |
my $first = $collection->first(qr/foo/); |
|
175 |
my $first = $collection->first(sub {...}); |
|
176 | ||
177 |
Evaluate regular expression or callback for each element in collection and |
|
178 |
return the first one that matched the regular expression, or for which the |
|
179 |
callback returned true. The element will be the first argument passed to the |
|
180 |
callback and is also available as C<$_>. |
|
181 | ||
182 |
my $five = $collection->first(sub { $_ == 5 }); |
|
183 | ||
184 |
=head2 flatten |
|
185 | ||
186 |
my $new = $collection->flatten; |
|
187 | ||
188 |
Flatten nested collections/arrays recursively and create a new collection with |
|
189 |
all elements. |
|
190 | ||
191 |
=head2 grep |
|
192 | ||
193 |
my $new = $collection->grep(qr/foo/); |
|
194 |
my $new = $collection->grep(sub {...}); |
|
195 | ||
196 |
Evaluate regular expression or callback for each element in collection and |
|
197 |
create a new collection with all elements that matched the regular expression, |
|
198 |
or for which the callback returned true. The element will be the first |
|
199 |
argument passed to the callback and is also available as C<$_>. |
|
200 | ||
201 |
my $interesting = $collection->grep(qr/mojo/i); |
|
202 | ||
203 |
=head2 join |
|
204 | ||
205 |
my $stream = $collection->join("\n"); |
|
206 | ||
207 |
Turn collection into L<Mojo::ByteStream>. |
|
208 | ||
209 |
$collection->join("\n")->say; |
|
210 | ||
211 |
=head2 map |
|
212 | ||
213 |
my $new = $collection->map(sub {...}); |
|
214 | ||
215 |
Evaluate callback for each element in collection and create a new collection |
|
216 |
from the results. The element will be the first argument passed to the |
|
217 |
callback and is also available as C<$_>. |
|
218 | ||
219 |
my $doubled = $collection->map(sub { $_ * 2 }); |
|
220 | ||
221 |
=head2 pluck |
|
222 | ||
223 |
my $new = $collection->pluck($method); |
|
224 |
my $new = $collection->pluck($method, @args); |
|
225 | ||
226 |
Call method on each element in collection and create a new collection from the |
|
227 |
results. |
|
228 | ||
229 |
# Equal to but more convenient than |
|
230 |
my $new = $collection->map(sub { $_->$method(@args) }); |
|
231 | ||
232 |
=head2 reverse |
|
233 | ||
234 |
my $new = $collection->reverse; |
|
235 | ||
236 |
Create a new collection with all elements in reverse order. |
|
237 | ||
238 |
=head2 slice |
|
239 | ||
240 |
my $new = $collection->slice(4 .. 7); |
|
241 | ||
242 |
Create a new collection with all selected elements. |
|
243 | ||
244 |
=head2 shuffle |
|
245 | ||
246 |
my $new = $collection->shuffle; |
|
247 | ||
248 |
Create a new collection with all elements in random order. |
|
249 | ||
250 |
=head2 size |
|
251 | ||
252 |
my $size = $collection->size; |
|
253 | ||
254 |
Number of elements in collection. |
|
255 | ||
256 |
=head2 sort |
|
257 | ||
258 |
my $new = $collection->sort; |
|
259 |
my $new = $collection->sort(sub {...}); |
|
260 | ||
261 |
Sort elements based on return value of callback and create a new collection |
|
262 |
from the results. |
|
263 | ||
264 |
my $insensitive = $collection->sort(sub { uc(shift) cmp uc(shift) }); |
|
265 | ||
266 |
=head2 tap |
|
267 | ||
268 |
$collection = $collection->tap(sub {...}); |
|
269 | ||
270 |
Alias for L<Mojo::Base/"tap">. |
|
271 | ||
272 |
=head2 uniq |
|
273 | ||
274 |
my $new = $collection->uniq; |
|
275 | ||
276 |
Create a new collection without duplicate elements. |
|
277 | ||
278 |
=head1 ELEMENT METHODS |
|
279 | ||
280 |
In addition to the methods above, you can also call methods provided by all |
|
281 |
elements in the collection directly and create a new collection from the |
|
282 |
results, similar to L</"pluck">. |
|
283 | ||
284 |
push @$collection, Mojo::DOM->new("<div><h1>$_</h1></div>") for 1 .. 9; |
|
285 |
say $collection->at('h1')->type('h2')->prepend_content('Test ')->root; |
|
286 | ||
287 |
=head1 ELEMENTS |
|
288 | ||
289 |
Direct array reference access to elements is also possible. |
|
290 | ||
291 |
say $collection->[23]; |
|
292 |
say for @$collection; |
|
293 | ||
294 |
=head1 SEE ALSO |
|
295 | ||
296 |
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
|
297 | ||
298 |
=cut |