biblesearch / mojo / lib / Mojo / Collection.pm /
Newer Older
298 lines | 6.712kb
add files
Yuki Kimoto authored on 2014-03-26
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