1
|
|
|
<?php |
2
|
|
|
declare(strict_types = 1); |
3
|
|
|
|
4
|
|
|
namespace Innmind\Immutable; |
5
|
|
|
|
6
|
|
|
use Innmind\Immutable\{ |
7
|
|
|
Exception\OutOfBoundException, |
8
|
|
|
Exception\LogicException, |
9
|
|
|
Exception\ElementNotFoundException, |
10
|
|
|
Exception\GroupEmptySequenceException |
11
|
|
|
}; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* A defined set of ordered elements |
15
|
|
|
*/ |
16
|
|
|
class Sequence implements SequenceInterface |
17
|
|
|
{ |
18
|
|
|
use Type; |
19
|
|
|
|
20
|
|
|
private $values; |
21
|
|
|
private $size; |
22
|
|
|
|
23
|
486 |
|
public function __construct(...$values) |
24
|
|
|
{ |
25
|
486 |
|
$this->values = $values; |
26
|
486 |
|
} |
27
|
|
|
|
28
|
3 |
|
public static function of(...$values): self |
29
|
|
|
{ |
30
|
3 |
|
return new self(...$values); |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* {@inheritdoc} |
35
|
|
|
*/ |
36
|
102 |
|
public function size(): int |
37
|
|
|
{ |
38
|
102 |
|
return $this->size ?? $this->size = count($this->values); |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* {@inheritdoc} |
43
|
|
|
*/ |
44
|
3 |
|
public function count(): int |
45
|
|
|
{ |
46
|
3 |
|
return $this->size(); |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* {@inheritdoc} |
51
|
|
|
*/ |
52
|
111 |
|
public function current() |
53
|
|
|
{ |
54
|
111 |
|
return current($this->values); |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* {@inheritdoc} |
59
|
|
|
*/ |
60
|
117 |
|
public function key() |
61
|
|
|
{ |
62
|
117 |
|
return key($this->values); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* {@inheritdoc} |
67
|
|
|
*/ |
68
|
111 |
|
public function next() |
69
|
|
|
{ |
70
|
111 |
|
next($this->values); |
71
|
111 |
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* {@inheritdoc} |
75
|
|
|
*/ |
76
|
111 |
|
public function rewind() |
77
|
|
|
{ |
78
|
111 |
|
reset($this->values); |
79
|
111 |
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* {@inheritdoc} |
83
|
|
|
*/ |
84
|
111 |
|
public function valid(): bool |
85
|
|
|
{ |
86
|
111 |
|
return $this->key() !== null; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* {@inheritdoc} |
91
|
|
|
*/ |
92
|
3 |
|
public function offsetExists($offset): bool |
93
|
|
|
{ |
94
|
3 |
|
return $this->has($offset); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* {@inheritdoc} |
99
|
|
|
*/ |
100
|
3 |
|
public function offsetGet($offset) |
101
|
|
|
{ |
102
|
3 |
|
return $this->get($offset); |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* {@inheritdoc} |
107
|
|
|
*/ |
108
|
3 |
|
public function offsetSet($offset, $value) |
109
|
|
|
{ |
110
|
3 |
|
throw new LogicException('You can\'t modify a sequence'); |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* {@inheritdoc} |
115
|
|
|
*/ |
116
|
3 |
|
public function offsetUnset($offset) |
117
|
|
|
{ |
118
|
3 |
|
throw new LogicException('You can\'t modify a sequence'); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* {@inheritdoc} |
123
|
|
|
*/ |
124
|
222 |
|
public function toPrimitive() |
125
|
|
|
{ |
126
|
222 |
|
return $this->values; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* {@inheritdoc} |
131
|
|
|
*/ |
132
|
87 |
|
public function get(int $index) |
133
|
|
|
{ |
134
|
87 |
|
if (!$this->has($index)) { |
135
|
9 |
|
throw new OutOfBoundException; |
136
|
|
|
} |
137
|
|
|
|
138
|
78 |
|
return $this->values[$index]; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* {@inheritdoc} |
143
|
|
|
*/ |
144
|
93 |
|
public function has(int $index): bool |
145
|
|
|
{ |
146
|
93 |
|
return array_key_exists($index, $this->values); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* {@inheritdoc} |
151
|
|
|
*/ |
152
|
9 |
|
public function diff(SequenceInterface $seq): SequenceInterface |
153
|
|
|
{ |
154
|
9 |
|
return new self( |
155
|
9 |
|
...array_diff( |
156
|
9 |
|
$this->values, |
157
|
9 |
|
$seq->toPrimitive() |
158
|
|
|
) |
159
|
|
|
); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* {@inheritdoc} |
164
|
|
|
*/ |
165
|
6 |
|
public function distinct(): SequenceInterface |
166
|
|
|
{ |
167
|
6 |
|
return new self(...array_unique($this->values)); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* {@inheritdoc} |
172
|
|
|
*/ |
173
|
27 |
|
public function drop(int $size): SequenceInterface |
174
|
|
|
{ |
175
|
27 |
|
return new self(...array_slice($this->values, $size)); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* {@inheritdoc} |
180
|
|
|
*/ |
181
|
6 |
|
public function dropEnd(int $size): SequenceInterface |
182
|
|
|
{ |
183
|
6 |
|
return new self(...array_slice($this->values, 0, $this->size() - $size)); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* {@inheritdoc} |
188
|
|
|
*/ |
189
|
36 |
|
public function equals(SequenceInterface $seq): bool |
190
|
|
|
{ |
191
|
36 |
|
return $this->values === $seq->toPrimitive(); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* {@inheritdoc} |
196
|
|
|
*/ |
197
|
9 |
|
public function filter(callable $predicate): SequenceInterface |
198
|
|
|
{ |
199
|
9 |
|
return new self(...array_filter( |
200
|
9 |
|
$this->values, |
201
|
9 |
|
$predicate |
202
|
|
|
)); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* {@inheritdoc} |
207
|
|
|
*/ |
208
|
9 |
|
public function foreach(callable $function): SequenceInterface |
209
|
|
|
{ |
210
|
9 |
|
foreach ($this->values as $value) { |
211
|
9 |
|
$function($value); |
212
|
|
|
} |
213
|
|
|
|
214
|
9 |
|
return $this; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* {@inheritdoc} |
219
|
|
|
*/ |
220
|
6 |
View Code Duplication |
public function groupBy(callable $discriminator): MapInterface |
|
|
|
|
221
|
|
|
{ |
222
|
6 |
|
if ($this->size() === 0) { |
223
|
3 |
|
throw new GroupEmptySequenceException; |
224
|
|
|
} |
225
|
|
|
|
226
|
3 |
|
$map = null; |
227
|
|
|
|
228
|
3 |
|
foreach ($this->values as $value) { |
229
|
3 |
|
$key = $discriminator($value); |
230
|
|
|
|
231
|
3 |
|
if ($map === null) { |
232
|
3 |
|
$map = new Map( |
233
|
3 |
|
$this->determineType($key), |
234
|
3 |
|
SequenceInterface::class |
235
|
|
|
); |
236
|
|
|
} |
237
|
|
|
|
238
|
3 |
|
if ($map->contains($key)) { |
239
|
3 |
|
$map = $map->put( |
240
|
3 |
|
$key, |
241
|
3 |
|
$map->get($key)->add($value) |
242
|
|
|
); |
243
|
|
|
} else { |
244
|
3 |
|
$map = $map->put($key, new self($value)); |
245
|
|
|
} |
246
|
|
|
} |
247
|
|
|
|
248
|
3 |
|
return $map; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* {@inheritdoc} |
253
|
|
|
*/ |
254
|
12 |
|
public function first() |
255
|
|
|
{ |
256
|
12 |
|
if ($this->size() === 0) { |
257
|
3 |
|
throw new OutOfBoundException; |
258
|
|
|
} |
259
|
|
|
|
260
|
9 |
|
return $this->values[0]; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* {@inheritdoc} |
265
|
|
|
*/ |
266
|
12 |
|
public function last() |
267
|
|
|
{ |
268
|
12 |
|
if ($this->size() === 0) { |
269
|
3 |
|
throw new OutOfBoundException; |
270
|
|
|
} |
271
|
|
|
|
272
|
9 |
|
return $this->values[$this->size() - 1]; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* {@inheritdoc} |
277
|
|
|
*/ |
278
|
156 |
|
public function contains($element): bool |
279
|
|
|
{ |
280
|
156 |
|
return in_array($element, $this->values, true); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* {@inheritdoc} |
285
|
|
|
*/ |
286
|
63 |
|
public function indexOf($element): int |
287
|
|
|
{ |
288
|
63 |
|
$index = array_search($element, $this->values, true); |
289
|
|
|
|
290
|
63 |
|
if ($index === false) { |
291
|
3 |
|
throw new ElementNotFoundException; |
292
|
|
|
} |
293
|
|
|
|
294
|
60 |
|
return $index; |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* {@inheritdoc} |
299
|
|
|
*/ |
300
|
6 |
|
public function indices(): StreamInterface |
301
|
|
|
{ |
302
|
6 |
|
$indices = new Stream('int'); |
303
|
|
|
|
304
|
6 |
|
foreach ($this->values as $index => $value) { |
305
|
6 |
|
$indices = $indices->add($index); |
306
|
|
|
} |
307
|
|
|
|
308
|
6 |
|
return $indices; |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* {@inheritdoc} |
313
|
|
|
*/ |
314
|
3 |
|
public function map(callable $function): SequenceInterface |
315
|
|
|
{ |
316
|
3 |
|
return new self(...array_map($function, $this->values)); |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* {@inheritdoc} |
321
|
|
|
*/ |
322
|
6 |
|
public function pad(int $size, $element): SequenceInterface |
323
|
|
|
{ |
324
|
6 |
|
return new self(...array_pad($this->values, $size, $element)); |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* {@inheritdoc} |
329
|
|
|
*/ |
330
|
3 |
|
public function partition(callable $predicate): MapInterface |
331
|
|
|
{ |
332
|
3 |
|
$truthy = []; |
333
|
3 |
|
$falsy = []; |
334
|
|
|
|
335
|
3 |
|
foreach ($this->values as $value) { |
336
|
3 |
|
if ($predicate($value) === true) { |
337
|
3 |
|
$truthy[] = $value; |
338
|
|
|
} else { |
339
|
3 |
|
$falsy[] = $value; |
340
|
|
|
} |
341
|
|
|
} |
342
|
|
|
|
343
|
3 |
|
return (new Map('bool', SequenceInterface::class)) |
344
|
3 |
|
->put(true, new self(...$truthy)) |
345
|
3 |
|
->put(false, new self(...$falsy)); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* {@inheritdoc} |
350
|
|
|
*/ |
351
|
51 |
|
public function slice(int $from, int $until): SequenceInterface |
352
|
|
|
{ |
353
|
51 |
|
return new self(...array_slice( |
354
|
51 |
|
$this->values, |
355
|
51 |
|
$from, |
356
|
51 |
|
$until - $from |
357
|
|
|
)); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* {@inheritdoc} |
362
|
|
|
*/ |
363
|
6 |
|
public function splitAt(int $index): StreamInterface |
364
|
|
|
{ |
365
|
6 |
|
return (new Stream(SequenceInterface::class)) |
366
|
6 |
|
->add($this->slice(0, $index)) |
367
|
6 |
|
->add($this->slice($index, $this->size())); |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
/** |
371
|
|
|
* {@inheritdoc} |
372
|
|
|
*/ |
373
|
27 |
|
public function take(int $size): SequenceInterface |
374
|
|
|
{ |
375
|
27 |
|
return $this->slice(0, $size); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* {@inheritdoc} |
380
|
|
|
*/ |
381
|
6 |
|
public function takeEnd(int $size): SequenceInterface |
382
|
|
|
{ |
383
|
6 |
|
return $this->slice($this->size() - $size, $this->size()); |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* {@inheritdoc} |
388
|
|
|
*/ |
389
|
33 |
|
public function append(SequenceInterface $seq): SequenceInterface |
390
|
|
|
{ |
391
|
33 |
|
return new self(...$this->values, ...$seq->toPrimitive()); |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
/** |
395
|
|
|
* {@inheritdoc} |
396
|
|
|
*/ |
397
|
9 |
|
public function intersect(SequenceInterface $seq): SequenceInterface |
398
|
|
|
{ |
399
|
9 |
|
return new self(...array_intersect($this->values, $seq->toPrimitive())); |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* {@inheritdoc} |
404
|
|
|
*/ |
405
|
24 |
|
public function join(string $separator): Str |
406
|
|
|
{ |
407
|
24 |
|
return new Str(implode($separator, $this->values)); |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
/** |
411
|
|
|
* {@inheritdoc} |
412
|
|
|
*/ |
413
|
297 |
|
public function add($element): SequenceInterface |
414
|
|
|
{ |
415
|
297 |
|
$values = $this->values; |
416
|
297 |
|
$values[] = $element; |
417
|
|
|
|
418
|
297 |
|
return new self(...$values); |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* {@inheritdoc} |
423
|
|
|
*/ |
424
|
9 |
|
public function sort(callable $function): SequenceInterface |
425
|
|
|
{ |
426
|
9 |
|
$values = $this->values; |
427
|
9 |
|
usort($values, $function); |
428
|
|
|
|
429
|
9 |
|
return new self(...$values); |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
/** |
433
|
|
|
* {@inheritdoc} |
434
|
|
|
*/ |
435
|
72 |
|
public function reduce($carry, callable $reducer) |
436
|
|
|
{ |
437
|
72 |
|
return array_reduce($this->values, $reducer, $carry); |
438
|
|
|
} |
439
|
|
|
|
440
|
|
|
/** |
441
|
|
|
* {@inheritdoc} |
442
|
|
|
*/ |
443
|
9 |
|
public function reverse(): SequenceInterface |
444
|
|
|
{ |
445
|
9 |
|
return new self(...array_reverse($this->values)); |
446
|
|
|
} |
447
|
|
|
} |
448
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.