|
1
|
|
|
<?php |
|
2
|
|
|
declare(strict_types = 1); |
|
3
|
|
|
|
|
4
|
|
|
namespace Innmind\Immutable; |
|
5
|
|
|
|
|
6
|
|
|
use Innmind\Immutable\{ |
|
7
|
|
|
Exception\InvalidArgumentException, |
|
8
|
|
|
Exception\LogicException, |
|
9
|
|
|
Exception\ElementNotFoundException, |
|
10
|
|
|
Exception\GroupEmptyMapException |
|
11
|
|
|
}; |
|
12
|
|
|
|
|
13
|
|
|
class Map implements MapInterface |
|
14
|
|
|
{ |
|
15
|
|
|
use Type; |
|
16
|
|
|
|
|
17
|
|
|
private $keyType; |
|
18
|
|
|
private $valueType; |
|
19
|
|
|
private $keySpec; |
|
20
|
|
|
private $valueSpec; |
|
21
|
|
|
private $keys; |
|
22
|
|
|
private $values; |
|
23
|
|
|
private $pairs; |
|
24
|
|
|
|
|
25
|
44 |
|
public function __construct(string $keyType, string $valueType) |
|
26
|
|
|
{ |
|
27
|
44 |
|
$this->keySpec = $this->getSpecFor($keyType); |
|
28
|
44 |
|
$this->valueSpec = $this->getSpecFor($valueType); |
|
29
|
44 |
|
$this->keyType = new StringPrimitive($keyType); |
|
|
|
|
|
|
30
|
44 |
|
$this->valueType = new StringPrimitive($valueType); |
|
|
|
|
|
|
31
|
44 |
|
$this->keys = new Sequence; |
|
32
|
44 |
|
$this->values = new Sequence; |
|
33
|
44 |
|
$this->pairs = new Sequence; |
|
34
|
44 |
|
} |
|
35
|
|
|
|
|
36
|
|
|
/** |
|
37
|
|
|
* {@inheritdoc} |
|
38
|
|
|
*/ |
|
39
|
18 |
|
public function keyType(): StringPrimitive |
|
40
|
|
|
{ |
|
41
|
18 |
|
return $this->keyType; |
|
42
|
|
|
} |
|
43
|
|
|
|
|
44
|
|
|
/** |
|
45
|
|
|
* {@inheritdoc} |
|
46
|
|
|
*/ |
|
47
|
17 |
|
public function valueType(): StringPrimitive |
|
48
|
|
|
{ |
|
49
|
17 |
|
return $this->valueType; |
|
50
|
|
|
} |
|
51
|
|
|
|
|
52
|
|
|
/** |
|
53
|
|
|
* {@inheritdoc} |
|
54
|
|
|
*/ |
|
55
|
11 |
|
public function size(): int |
|
56
|
|
|
{ |
|
57
|
11 |
|
return $this->keys->size(); |
|
58
|
|
|
} |
|
59
|
|
|
|
|
60
|
|
|
/** |
|
61
|
|
|
* {@inheritdoc} |
|
62
|
|
|
*/ |
|
63
|
2 |
|
public function count(): int |
|
64
|
|
|
{ |
|
65
|
2 |
|
return $this->keys->count(); |
|
66
|
|
|
} |
|
67
|
|
|
|
|
68
|
|
|
/** |
|
69
|
|
|
* {@inheritdoc} |
|
70
|
|
|
*/ |
|
71
|
1 |
|
public function current() |
|
72
|
|
|
{ |
|
73
|
1 |
|
return $this->values->current(); |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
/** |
|
77
|
|
|
* {@inheritdoc} |
|
78
|
|
|
*/ |
|
79
|
1 |
|
public function key() |
|
80
|
|
|
{ |
|
81
|
1 |
|
return $this->keys->current(); |
|
82
|
|
|
} |
|
83
|
|
|
|
|
84
|
|
|
/** |
|
85
|
|
|
* {@inheritdoc} |
|
86
|
|
|
*/ |
|
87
|
1 |
|
public function next() |
|
88
|
|
|
{ |
|
89
|
1 |
|
$this->keys->next(); |
|
90
|
1 |
|
$this->values->next(); |
|
91
|
1 |
|
$this->pairs->next(); |
|
92
|
1 |
|
} |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* {@inheritdoc} |
|
96
|
|
|
*/ |
|
97
|
1 |
|
public function rewind() |
|
98
|
|
|
{ |
|
99
|
1 |
|
$this->keys->rewind(); |
|
100
|
1 |
|
$this->values->rewind(); |
|
101
|
1 |
|
$this->pairs->rewind(); |
|
102
|
1 |
|
} |
|
103
|
|
|
|
|
104
|
|
|
/** |
|
105
|
|
|
* {@inheritdoc} |
|
106
|
|
|
*/ |
|
107
|
1 |
|
public function valid() |
|
108
|
|
|
{ |
|
109
|
1 |
|
return $this->keys->valid(); |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
/** |
|
113
|
|
|
* {@inheritdoc} |
|
114
|
|
|
*/ |
|
115
|
1 |
|
public function offsetExists($offset): bool |
|
116
|
|
|
{ |
|
117
|
1 |
|
return $this->keys->contains($offset); |
|
118
|
|
|
} |
|
119
|
|
|
|
|
120
|
|
|
/** |
|
121
|
|
|
* {@inheritdoc} |
|
122
|
|
|
*/ |
|
123
|
4 |
|
public function offsetGet($offset) |
|
124
|
|
|
{ |
|
125
|
4 |
|
return $this->get($offset); |
|
126
|
|
|
} |
|
127
|
|
|
|
|
128
|
|
|
/** |
|
129
|
|
|
* {@inheritdoc} |
|
130
|
|
|
*/ |
|
131
|
1 |
|
public function offsetSet($offset, $value) |
|
132
|
|
|
{ |
|
133
|
1 |
|
throw new LogicException('You can\'t modify a map'); |
|
134
|
|
|
} |
|
135
|
|
|
|
|
136
|
|
|
/** |
|
137
|
|
|
* {@inheritdoc} |
|
138
|
|
|
*/ |
|
139
|
1 |
|
public function offsetUnset($offset) |
|
140
|
|
|
{ |
|
141
|
1 |
|
throw new LogicException('You can\'t modify a map'); |
|
142
|
|
|
} |
|
143
|
|
|
|
|
144
|
|
|
/** |
|
145
|
|
|
* {@inheritdoc} |
|
146
|
|
|
*/ |
|
147
|
37 |
|
public function put($key, $value): MapInterface |
|
148
|
|
|
{ |
|
149
|
37 |
|
$this->keySpec->validate($key); |
|
150
|
36 |
|
$this->valueSpec->validate($value); |
|
151
|
|
|
|
|
152
|
34 |
|
$map = clone $this; |
|
153
|
|
|
|
|
154
|
34 |
|
if ($this->keys->contains($key)) { |
|
155
|
7 |
|
$index = $this->keys->indexOf($key); |
|
156
|
7 |
|
$map->values = (new Sequence) |
|
157
|
7 |
|
->append($this->values->take($index)) |
|
158
|
7 |
|
->add($value) |
|
159
|
7 |
|
->append($this->values->drop($index + 1)); |
|
160
|
7 |
|
$map->pairs = (new Sequence) |
|
161
|
7 |
|
->append($this->pairs->take($index)) |
|
162
|
7 |
|
->add(new Pair($key, $value)) |
|
163
|
7 |
|
->append($this->pairs->drop($index + 1)); |
|
164
|
|
|
} else { |
|
165
|
34 |
|
$map->keys = $this->keys->add($key); |
|
166
|
34 |
|
$map->values = $this->values->add($value); |
|
167
|
34 |
|
$map->pairs = $this->pairs->add(new Pair($key, $value)); |
|
168
|
|
|
} |
|
169
|
|
|
|
|
170
|
34 |
|
return $map; |
|
|
|
|
|
|
171
|
|
|
} |
|
172
|
|
|
|
|
173
|
|
|
/** |
|
174
|
|
|
* {@inheritdoc} |
|
175
|
|
|
*/ |
|
176
|
16 |
|
public function get($key) |
|
177
|
|
|
{ |
|
178
|
16 |
|
if (!$this->keys->contains($key)) { |
|
179
|
2 |
|
throw new ElementNotFoundException; |
|
180
|
|
|
} |
|
181
|
|
|
|
|
182
|
14 |
|
return $this->values->get( |
|
183
|
14 |
|
$this->keys->indexOf($key) |
|
184
|
|
|
); |
|
185
|
|
|
} |
|
186
|
|
|
|
|
187
|
|
|
/** |
|
188
|
|
|
* {@inheritdoc} |
|
189
|
|
|
*/ |
|
190
|
11 |
|
public function contains($key): bool |
|
191
|
|
|
{ |
|
192
|
11 |
|
return $this->keys->contains($key); |
|
193
|
|
|
} |
|
194
|
|
|
|
|
195
|
|
|
/** |
|
196
|
|
|
* {@inheritdoc} |
|
197
|
|
|
*/ |
|
198
|
1 |
View Code Duplication |
public function drop(int $size): MapInterface |
|
|
|
|
|
|
199
|
|
|
{ |
|
200
|
1 |
|
$map = clone $this; |
|
201
|
1 |
|
$map->keys = $this->keys->drop($size); |
|
202
|
1 |
|
$map->values = $this->values->drop($size); |
|
203
|
1 |
|
$map->pairs = $this->pairs->drop($size); |
|
204
|
|
|
|
|
205
|
1 |
|
return $map; |
|
|
|
|
|
|
206
|
|
|
} |
|
207
|
|
|
|
|
208
|
|
|
/** |
|
209
|
|
|
* {@inheritdoc} |
|
210
|
|
|
*/ |
|
211
|
1 |
View Code Duplication |
public function dropEnd(int $size): MapInterface |
|
|
|
|
|
|
212
|
|
|
{ |
|
213
|
1 |
|
$map = clone $this; |
|
214
|
1 |
|
$map->keys = $this->keys->dropEnd($size); |
|
215
|
1 |
|
$map->values = $this->values->dropEnd($size); |
|
216
|
1 |
|
$map->pairs = $this->pairs->dropEnd($size); |
|
217
|
|
|
|
|
218
|
1 |
|
return $map; |
|
|
|
|
|
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
|
|
/** |
|
222
|
|
|
* {@inheritdoc} |
|
223
|
|
|
*/ |
|
224
|
6 |
|
public function clear(): MapInterface |
|
225
|
|
|
{ |
|
226
|
6 |
|
$map = clone $this; |
|
227
|
6 |
|
$map->keys = new Sequence; |
|
228
|
6 |
|
$map->values = new Sequence; |
|
229
|
6 |
|
$map->pairs = new Sequence; |
|
230
|
|
|
|
|
231
|
6 |
|
return $map; |
|
|
|
|
|
|
232
|
|
|
} |
|
233
|
|
|
|
|
234
|
|
|
/** |
|
235
|
|
|
* {@inheritdoc} |
|
236
|
|
|
*/ |
|
237
|
2 |
|
public function equals(MapInterface $map): bool |
|
238
|
|
|
{ |
|
239
|
2 |
|
if (!$map->keys()->equals($this->keys())) { |
|
240
|
2 |
|
return false; |
|
241
|
|
|
} |
|
242
|
|
|
|
|
243
|
1 |
|
return $map->values()->equals($this->values()); |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
/** |
|
247
|
|
|
* {@inheritdoc} |
|
248
|
|
|
*/ |
|
249
|
1 |
|
public function filter(callable $predicate): MapInterface |
|
250
|
|
|
{ |
|
251
|
1 |
|
$map = $this->clear(); |
|
252
|
|
|
|
|
253
|
1 |
|
foreach ($this->pairs as $pair) { |
|
254
|
1 |
|
if ($predicate($pair->key(), $pair->value()) === true) { |
|
255
|
1 |
|
$map->keys = $map->keys->add($pair->key()); |
|
256
|
1 |
|
$map->values = $map->values->add($pair->value()); |
|
257
|
1 |
|
$map->pairs = $map->pairs->add($pair); |
|
258
|
|
|
} |
|
259
|
|
|
} |
|
260
|
|
|
|
|
261
|
1 |
|
return $map; |
|
262
|
|
|
} |
|
263
|
|
|
|
|
264
|
|
|
/** |
|
265
|
|
|
* {@inheritdoc} |
|
266
|
|
|
*/ |
|
267
|
|
|
public function foreach(callable $function): MapInterface |
|
268
|
|
|
{ |
|
269
|
1 |
|
foreach ($this->pairs as $pair) { |
|
270
|
1 |
|
$function($pair->key(), $pair->value()); |
|
271
|
|
|
} |
|
272
|
|
|
|
|
273
|
1 |
|
return $this; |
|
|
|
|
|
|
274
|
|
|
} |
|
275
|
|
|
|
|
276
|
|
|
/** |
|
277
|
|
|
* {@inheritdoc} |
|
278
|
|
|
*/ |
|
279
|
2 |
View Code Duplication |
public function groupBy(callable $discriminator): MapInterface |
|
280
|
|
|
{ |
|
281
|
2 |
|
if ($this->size() === 0) { |
|
282
|
1 |
|
throw new GroupEmptyMapException; |
|
283
|
|
|
} |
|
284
|
|
|
|
|
285
|
1 |
|
$map = null; |
|
286
|
|
|
|
|
287
|
1 |
|
foreach ($this->pairs as $pair) { |
|
288
|
1 |
|
$key = $discriminator($pair->key(), $pair->value()); |
|
289
|
|
|
|
|
290
|
1 |
|
if ($map === null) { |
|
291
|
1 |
|
$type = gettype($key); |
|
292
|
1 |
|
$map = new self( |
|
293
|
1 |
|
$type === 'object' ? get_class($key) : $type, |
|
294
|
1 |
|
SequenceInterface::class |
|
295
|
|
|
); |
|
296
|
|
|
} |
|
297
|
|
|
|
|
298
|
1 |
|
if ($map->contains($key)) { |
|
299
|
1 |
|
$map = $map->put( |
|
300
|
|
|
$key, |
|
301
|
1 |
|
$map->get($key)->add($pair) |
|
302
|
|
|
); |
|
303
|
|
|
} else { |
|
304
|
1 |
|
$map = $map->put($key, new Sequence($pair)); |
|
305
|
|
|
} |
|
306
|
|
|
} |
|
307
|
|
|
|
|
308
|
1 |
|
return $map; |
|
309
|
|
|
} |
|
310
|
|
|
|
|
311
|
|
|
/** |
|
312
|
|
|
* {@inheritdoc} |
|
313
|
|
|
*/ |
|
314
|
1 |
|
public function first(): Pair |
|
315
|
|
|
{ |
|
316
|
1 |
|
return $this->pairs->first(); |
|
317
|
|
|
} |
|
318
|
|
|
|
|
319
|
|
|
/** |
|
320
|
|
|
* {@inheritdoc} |
|
321
|
|
|
*/ |
|
322
|
1 |
|
public function last(): Pair |
|
323
|
|
|
{ |
|
324
|
1 |
|
return $this->pairs->last(); |
|
325
|
|
|
} |
|
326
|
|
|
|
|
327
|
|
|
/** |
|
328
|
|
|
* {@inheritdoc} |
|
329
|
|
|
*/ |
|
330
|
9 |
|
public function keys(): SequenceInterface |
|
331
|
|
|
{ |
|
332
|
9 |
|
return $this->keys; |
|
333
|
|
|
} |
|
334
|
|
|
|
|
335
|
|
|
/** |
|
336
|
|
|
* {@inheritdoc} |
|
337
|
|
|
*/ |
|
338
|
7 |
|
public function values(): SequenceInterface |
|
339
|
|
|
{ |
|
340
|
7 |
|
return $this->values; |
|
341
|
|
|
} |
|
342
|
|
|
|
|
343
|
|
|
/** |
|
344
|
|
|
* {@inheritdoc} |
|
345
|
|
|
*/ |
|
346
|
3 |
|
public function map(callable $function): MapInterface |
|
347
|
|
|
{ |
|
348
|
3 |
|
$map = $this->clear(); |
|
349
|
|
|
|
|
350
|
3 |
|
foreach ($this->pairs as $pair) { |
|
351
|
3 |
|
$return = $function( |
|
352
|
3 |
|
$pair->key(), |
|
353
|
3 |
|
$pair->value() |
|
354
|
|
|
); |
|
355
|
|
|
|
|
356
|
3 |
|
if ($return instanceof Pair) { |
|
357
|
2 |
|
$key = $return->key(); |
|
358
|
2 |
|
$value = $return->value(); |
|
359
|
|
|
} else { |
|
360
|
2 |
|
$key = $pair->key(); |
|
361
|
2 |
|
$value = $return; |
|
362
|
|
|
} |
|
363
|
|
|
|
|
364
|
3 |
|
$map = $map->put($key, $value); |
|
365
|
|
|
} |
|
366
|
|
|
|
|
367
|
1 |
|
return $map; |
|
368
|
|
|
} |
|
369
|
|
|
|
|
370
|
|
|
/** |
|
371
|
|
|
* {@inheritdoc} |
|
372
|
|
|
*/ |
|
373
|
1 |
View Code Duplication |
public function take(int $size): MapInterface |
|
|
|
|
|
|
374
|
|
|
{ |
|
375
|
1 |
|
$map = clone $this; |
|
376
|
1 |
|
$map->keys = $this->keys->take($size); |
|
377
|
1 |
|
$map->values = $this->values->take($size); |
|
378
|
1 |
|
$map->pairs = $this->pairs->take($size); |
|
379
|
|
|
|
|
380
|
1 |
|
return $map; |
|
|
|
|
|
|
381
|
|
|
} |
|
382
|
|
|
|
|
383
|
|
|
/** |
|
384
|
|
|
* {@inheritdoc} |
|
385
|
|
|
*/ |
|
386
|
1 |
View Code Duplication |
public function takeEnd(int $size): MapInterface |
|
|
|
|
|
|
387
|
|
|
{ |
|
388
|
1 |
|
$map = clone $this; |
|
389
|
1 |
|
$map->keys = $this->keys->takeEnd($size); |
|
390
|
1 |
|
$map->values = $this->values->takeEnd($size); |
|
391
|
1 |
|
$map->pairs = $this->pairs->takeEnd($size); |
|
392
|
|
|
|
|
393
|
1 |
|
return $map; |
|
|
|
|
|
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
|
|
/** |
|
397
|
|
|
* {@inheritdoc} |
|
398
|
|
|
*/ |
|
399
|
1 |
|
public function join(string $separator): StringPrimitive |
|
400
|
|
|
{ |
|
401
|
1 |
|
return $this->values->join($separator); |
|
402
|
|
|
} |
|
403
|
|
|
|
|
404
|
|
|
/** |
|
405
|
|
|
* {@inheritdoc} |
|
406
|
|
|
*/ |
|
407
|
1 |
|
public function remove($key): MapInterface |
|
408
|
|
|
{ |
|
409
|
1 |
|
if (!$this->contains($key)) { |
|
410
|
1 |
|
return $this; |
|
|
|
|
|
|
411
|
|
|
} |
|
412
|
|
|
|
|
413
|
1 |
|
$index = $this->keys->indexOf($key); |
|
414
|
1 |
|
$map = clone $this; |
|
415
|
1 |
|
$map->keys = $this |
|
416
|
1 |
|
->keys |
|
417
|
1 |
|
->slice(0, $index) |
|
418
|
1 |
|
->append($this->keys->slice($index + 1, $this->keys->size())); |
|
419
|
1 |
|
$map->values = $this |
|
420
|
1 |
|
->values |
|
421
|
1 |
|
->slice(0, $index) |
|
422
|
1 |
|
->append($this->values->slice($index + 1, $this->values->size())); |
|
423
|
1 |
|
$map->pairs = $this |
|
424
|
1 |
|
->pairs |
|
425
|
1 |
|
->slice(0, $index) |
|
426
|
1 |
|
->append($this->pairs->slice($index + 1, $this->pairs->size())); |
|
427
|
|
|
|
|
428
|
1 |
|
return $map; |
|
|
|
|
|
|
429
|
|
|
} |
|
430
|
|
|
|
|
431
|
|
|
/** |
|
432
|
|
|
* {@inheritdoc} |
|
433
|
|
|
*/ |
|
434
|
2 |
|
public function merge(MapInterface $map): MapInterface |
|
435
|
|
|
{ |
|
436
|
|
|
if ( |
|
437
|
2 |
|
!$this->keyType()->equals($map->keyType()) || |
|
438
|
2 |
|
!$this->valueType()->equals($map->valueType()) |
|
439
|
|
|
) { |
|
440
|
1 |
|
throw new InvalidArgumentException( |
|
441
|
1 |
|
'The 2 maps does not reference the same types' |
|
442
|
|
|
); |
|
443
|
|
|
} |
|
444
|
|
|
|
|
445
|
1 |
|
return $map->reduce( |
|
446
|
1 |
|
clone $this, |
|
447
|
1 |
|
function(self $carry, $key, $value): self { |
|
448
|
1 |
|
return $carry->put($key, $value); |
|
449
|
1 |
|
} |
|
450
|
|
|
); |
|
451
|
|
|
} |
|
452
|
|
|
|
|
453
|
|
|
/** |
|
454
|
|
|
* {@inheritdoc} |
|
455
|
|
|
*/ |
|
456
|
1 |
|
public function partition(callable $predicate): MapInterface |
|
457
|
|
|
{ |
|
458
|
1 |
|
$truthy = $this->clear(); |
|
459
|
1 |
|
$falsy = $this->clear(); |
|
460
|
|
|
|
|
461
|
1 |
|
foreach ($this->pairs as $pair) { |
|
462
|
1 |
|
$return = $predicate( |
|
463
|
1 |
|
$pair->key(), |
|
464
|
1 |
|
$pair->value() |
|
465
|
|
|
); |
|
466
|
|
|
|
|
467
|
1 |
|
if ($return === true) { |
|
468
|
1 |
|
$truthy = $truthy->put($pair->key(), $pair->value()); |
|
469
|
|
|
} else { |
|
470
|
1 |
|
$falsy = $falsy->put($pair->key(), $pair->value()); |
|
471
|
|
|
} |
|
472
|
|
|
} |
|
473
|
|
|
|
|
474
|
1 |
|
return (new self('bool', MapInterface::class)) |
|
475
|
1 |
|
->put(true, $truthy) |
|
476
|
1 |
|
->put(false, $falsy); |
|
477
|
|
|
} |
|
478
|
|
|
|
|
479
|
|
|
/** |
|
480
|
|
|
* {@inheritdoc} |
|
481
|
|
|
*/ |
|
482
|
2 |
|
public function reduce($carry, callable $reducer) |
|
483
|
|
|
{ |
|
484
|
2 |
|
foreach ($this->pairs as $pair) { |
|
485
|
2 |
|
$carry = $reducer($carry, $pair->key(), $pair->value()); |
|
486
|
|
|
} |
|
487
|
|
|
|
|
488
|
2 |
|
return $carry; |
|
489
|
|
|
} |
|
490
|
|
|
} |
|
491
|
|
|
|
This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.