4
4
5
5
namespace GeekCell \Ddd \Domain ;
6
6
7
+ use ArrayAccess ;
8
+ use ArrayIterator ;
7
9
use Assert ;
10
+ use Countable ;
11
+ use InvalidArgumentException ;
12
+ use IteratorAggregate ;
13
+ use Traversable ;
14
+ use function array_filter ;
15
+ use function array_map ;
16
+ use function array_reduce ;
17
+ use function array_values ;
18
+ use function count ;
19
+ use function get_class ;
20
+ use function is_int ;
21
+ use function reset ;
8
22
9
23
/**
10
24
* @template T of object
11
- * @implements \ IteratorAggregate<T>
12
- * @implements \ ArrayAccess<mixed, T>
25
+ * @implements IteratorAggregate<T>
26
+ * @implements ArrayAccess<mixed, T>
13
27
*/
14
- class Collection implements \ ArrayAccess, \ Countable, \ IteratorAggregate
28
+ class Collection implements ArrayAccess, Countable, IteratorAggregate
15
29
{
16
30
/**
17
31
* @param T[] $items
@@ -26,11 +40,218 @@ final public function __construct(
26
40
}
27
41
}
28
42
43
+ /**
44
+ * Creates a collection from a given iterable of items.
45
+ * This function is useful when trying to create a collection from a generator or an iterator.
46
+ *
47
+ * @param iterable<T> $items
48
+ * @param class-string<T>|null $itemType
49
+ * @return self<T>
50
+ * @throws Assert\AssertionFailedException
51
+ */
52
+ public static function fromIterable (iterable $ items , ?string $ itemType = null ): static
53
+ {
54
+ if (is_array ($ items )) {
55
+ return new static ($ items , $ itemType );
56
+ }
57
+
58
+ if (!$ items instanceof Traversable) {
59
+ $ items = [...$ items ];
60
+ }
61
+
62
+ return new static (iterator_to_array ($ items ), $ itemType );
63
+ }
64
+
65
+ /**
66
+ * Returns true if every value in the collection passes the callback truthy test. Opposite of self::none().
67
+ * Callback arguments will be element, index, collection.
68
+ * Function short-circuits on first falsy return value.
69
+ *
70
+ * @param ?callable(T, int, static): bool $callback
71
+ * @return bool
72
+ */
73
+ public function every (callable $ callback = null ): bool
74
+ {
75
+ if ($ callback === null ) {
76
+ $ callback = static fn ($ item , $ index , $ self ) => $ item ;
77
+ }
78
+
79
+ foreach ($ this ->items as $ index => $ item ) {
80
+ if (!$ callback ($ item , $ index , $ this )) {
81
+ return false ;
82
+ }
83
+ }
84
+
85
+ return true ;
86
+ }
87
+
88
+ /**
89
+ * Returns true if every value in the collection passes the callback falsy test. Opposite of self::every().
90
+ * Callback arguments will be element, index, collection.
91
+ * Function short-circuits on first truthy return value.
92
+ *
93
+ * @param ?callable(T, int, static): bool $callback
94
+ * @return bool
95
+ */
96
+ public function none (callable $ callback = null ): bool
97
+ {
98
+ if ($ callback === null ) {
99
+ $ callback = static fn ($ item , $ index , $ self ) => $ item ;
100
+ }
101
+
102
+ foreach ($ this ->items as $ index => $ item ) {
103
+ if ($ callback ($ item , $ index , $ this )) {
104
+ return false ;
105
+ }
106
+ }
107
+
108
+ return true ;
109
+ }
110
+
111
+ /**
112
+ * Returns true if at least one value in the collection passes the callback truthy test.
113
+ * Callback arguments will be element, index, collection.
114
+ * Function short-circuits on first truthy return value.
115
+ *
116
+ * @param ?callable(T, int, static): bool $callback
117
+ * @return bool
118
+ */
119
+ public function some (callable $ callback = null ): bool
120
+ {
121
+ if ($ callback === null ) {
122
+ $ callback = static fn ($ item , $ index , $ self ) => $ item ;
123
+ }
124
+
125
+ foreach ($ this ->items as $ index => $ item ) {
126
+ if ($ callback ($ item , $ index , $ this )) {
127
+ return true ;
128
+ }
129
+ }
130
+
131
+ return false ;
132
+ }
133
+
134
+ /**
135
+ * Returns the first element of the collection that matches the given callback.
136
+ * If no callback is given the first element in the collection is returned.
137
+ * Throws exception if collection is empty or the given callback was never satisfied.
138
+ *
139
+ * @param ?callable(T, int, static): bool $callback
140
+ * @return T
141
+ * @throws InvalidArgumentException
142
+ */
143
+ public function first (callable $ callback = null )
144
+ {
145
+ if ($ this ->items === []) {
146
+ throw new InvalidArgumentException ('No items in collection ' );
147
+ }
148
+
149
+ foreach ($ this ->items as $ index => $ item ) {
150
+ if ($ callback === null || $ callback ($ item , $ index , $ this )) {
151
+ return $ item ;
152
+ }
153
+ }
154
+
155
+ throw new InvalidArgumentException ('No item found in collection that satisfies first callback ' );
156
+ }
157
+
158
+ /**
159
+ * Returns the first element of the collection that matches the given callback.
160
+ * If no callback is given the first element in the collection is returned.
161
+ * If the collection is empty the given fallback value is returned instead.
162
+ *
163
+ * @template U of T|mixed
164
+ * @param ?callable(T, int, static): bool $callback
165
+ * @param U $fallbackValue
166
+ * @return U
167
+ * @throws InvalidArgumentException
168
+ */
169
+ public function firstOr (callable $ callback = null , mixed $ fallbackValue = null )
170
+ {
171
+ if ($ this ->items === []) {
172
+ return $ fallbackValue ;
173
+ }
174
+
175
+ foreach ($ this ->items as $ index => $ item ) {
176
+ if ($ callback === null || $ callback ($ item , $ index , $ this )) {
177
+ return $ item ;
178
+ }
179
+ }
180
+
181
+ return $ fallbackValue ;
182
+ }
183
+
184
+ /**
185
+ * Returns the last element of the collection that matches the given callback.
186
+ * If no callback is given the last element in the collection is returned.
187
+ * Throws exception if collection is empty or the given callback was never satisfied.
188
+ *
189
+ * @param ?callable(T, int, static): bool $callback
190
+ * @return T
191
+ * @throws InvalidArgumentException
192
+ */
193
+ public function last (callable $ callback = null )
194
+ {
195
+ if ($ this ->items === []) {
196
+ throw new InvalidArgumentException ('No items in collection ' );
197
+ }
198
+
199
+ foreach (array_reverse ($ this ->items ) as $ index => $ item ) {
200
+ if ($ callback === null || $ callback ($ item , $ index , $ this )) {
201
+ return $ item ;
202
+ }
203
+ }
204
+
205
+ throw new InvalidArgumentException ('No item found in collection that satisfies last callback ' );
206
+ }
207
+
208
+ /**
209
+ * Returns the last element of the collection that matches the given callback.
210
+ * If no callback is given the last element in the collection is returned.
211
+ * If the collection is empty the given fallback value is returned instead.
212
+ *
213
+ * @template U of T|mixed
214
+ * @param ?callable(T, int, static): bool $callback
215
+ * @param U $fallbackValue
216
+ * @return U
217
+ * @throws InvalidArgumentException
218
+ */
219
+ public function lastOr (callable $ callback = null , mixed $ fallbackValue = null )
220
+ {
221
+ if ($ this ->items === []) {
222
+ return $ fallbackValue ;
223
+ }
224
+
225
+ foreach (array_reverse ($ this ->items ) as $ index => $ item ) {
226
+ if ($ callback === null || $ callback ($ item , $ index , $ this )) {
227
+ return $ item ;
228
+ }
229
+ }
230
+
231
+ return $ fallbackValue ;
232
+ }
233
+
234
+ /**
235
+ * Returns whether the collection is empty (has no items)
236
+ */
237
+ public function isEmpty (): bool
238
+ {
239
+ return $ this ->items === [];
240
+ }
241
+
242
+ /**
243
+ * Returns whether the collection has items
244
+ */
245
+ public function hasItems (): bool
246
+ {
247
+ return $ this ->items !== [];
248
+ }
249
+
29
250
/**
30
251
* Add one or more items to the collection. It **does not** modify the
31
252
* current collection, but returns a new one.
32
253
*
33
- * @param mixed $item One or more items to add to the collection.
254
+ * @param T|iterable<T> $item One or more items to add to the collection.
34
255
* @return static
35
256
*/
36
257
public function add (mixed $ item ): static
@@ -56,7 +277,7 @@ public function add(mixed $item): static
56
277
public function filter (callable $ callback ): static
57
278
{
58
279
return new static (
59
- \ array_values (\ array_filter ($ this ->items , $ callback )),
280
+ array_values (array_filter ($ this ->items , $ callback )),
60
281
$ this ->itemType ,
61
282
);
62
283
}
@@ -74,15 +295,15 @@ public function filter(callable $callback): static
74
295
*/
75
296
public function map (callable $ callback , bool $ inferTypes = true ): static
76
297
{
77
- $ mapResult = \ array_map ($ callback , $ this ->items );
78
- $ firstItem = \ reset ($ mapResult );
298
+ $ mapResult = array_map ($ callback , $ this ->items );
299
+ $ firstItem = reset ($ mapResult );
79
300
80
301
if ($ firstItem === false || !is_object ($ firstItem )) {
81
302
return new static ($ mapResult );
82
303
}
83
304
84
305
if ($ inferTypes && $ this ->itemType !== null ) {
85
- return new static ($ mapResult , \ get_class ($ firstItem ));
306
+ return new static ($ mapResult , get_class ($ firstItem ));
86
307
}
87
308
88
309
return new static ($ mapResult );
@@ -98,15 +319,15 @@ public function map(callable $callback, bool $inferTypes = true): static
98
319
*/
99
320
public function reduce (callable $ callback , mixed $ initial = null ): mixed
100
321
{
101
- return \ array_reduce ($ this ->items , $ callback , $ initial );
322
+ return array_reduce ($ this ->items , $ callback , $ initial );
102
323
}
103
324
104
325
/**
105
326
* @inheritDoc
106
327
*/
107
328
public function offsetExists (mixed $ offset ): bool
108
329
{
109
- if (!\ is_int ($ offset )) {
330
+ if (!is_int ($ offset )) {
110
331
return false ;
111
332
}
112
333
@@ -152,14 +373,14 @@ public function offsetUnset(mixed $offset): void
152
373
*/
153
374
public function count (): int
154
375
{
155
- return \ count ($ this ->items );
376
+ return count ($ this ->items );
156
377
}
157
378
158
379
/**
159
380
* @inheritDoc
160
381
*/
161
- public function getIterator (): \ Traversable
382
+ public function getIterator (): Traversable
162
383
{
163
- return new \ ArrayIterator ($ this ->items );
384
+ return new ArrayIterator ($ this ->items );
164
385
}
165
386
}
0 commit comments