1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @license https://github.com/f500/equatable/blob/master/LICENSE MIT |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
declare(strict_types=1); |
8
|
|
|
|
9
|
|
|
namespace F500\Equatable; |
10
|
|
|
|
11
|
|
|
use F500\Equatable\Exceptions\InRangeException; |
12
|
|
|
use F500\Equatable\Exceptions\InvalidArgumentException; |
13
|
|
|
use F500\Equatable\Exceptions\OutOfRangeException; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* @copyright Copyright (c) 2015 Future500 B.V. |
17
|
|
|
* @author Jasper N. Brouwer <[email protected]> |
18
|
|
|
*/ |
19
|
|
|
final class Map extends Collection |
20
|
|
|
{ |
21
|
276 |
|
public function __construct(array $values = []) |
22
|
|
|
{ |
23
|
276 |
|
foreach ($values as $key => $value) { |
24
|
255 |
|
$this->guardAgainstInvalidKey($key); |
25
|
252 |
|
$this->guardAgainstInvalidValue($value); |
26
|
249 |
|
$this->items[$key] = $value; |
27
|
|
|
} |
28
|
270 |
|
} |
29
|
|
|
|
30
|
3 |
View Code Duplication |
public function __clone() |
|
|
|
|
31
|
|
|
{ |
32
|
3 |
|
$items = []; |
33
|
|
|
|
34
|
3 |
|
foreach ($this->items as $key => $item) { |
35
|
3 |
|
if (is_object($item)) { |
36
|
3 |
|
$items[$key] = clone $item; |
37
|
|
|
} else { |
38
|
3 |
|
$items[$key] = $item; |
39
|
|
|
} |
40
|
|
|
} |
41
|
|
|
|
42
|
3 |
|
$this->items = $items; |
43
|
3 |
|
} |
44
|
|
|
|
45
|
132 |
|
public function get(string $key) |
|
|
|
|
46
|
|
|
{ |
47
|
132 |
|
if (!$this->containsKey($key)) { |
48
|
3 |
|
throw OutOfRangeException::doesNotContainKey($key); |
49
|
|
|
} |
50
|
|
|
|
51
|
129 |
|
return $this->items[$key]; |
52
|
|
|
} |
53
|
|
|
|
54
|
144 |
|
public function containsKey(string $key): bool |
55
|
|
|
{ |
56
|
144 |
|
return isset($this->items[$key]); |
57
|
|
|
} |
58
|
|
|
|
59
|
63 |
View Code Duplication |
public function search($value): string |
|
|
|
|
60
|
|
|
{ |
61
|
63 |
|
foreach ($this->items as $key => $item) { |
62
|
63 |
|
if ($this->theseAreEqual($item, $value)) { |
63
|
63 |
|
return $key; |
64
|
|
|
} |
65
|
|
|
} |
66
|
|
|
|
67
|
30 |
|
throw OutOfRangeException::doesNotContainValue($value); |
68
|
|
|
} |
69
|
|
|
|
70
|
42 |
|
public function equals($other): bool |
71
|
|
|
{ |
72
|
42 |
|
if (!$other instanceof static) { |
73
|
3 |
|
return false; |
74
|
|
|
} |
75
|
|
|
|
76
|
39 |
|
if ($this->count() !== $other->count()) { |
77
|
6 |
|
return false; |
78
|
|
|
} |
79
|
|
|
|
80
|
33 |
|
foreach ($this->items as $key => $item) { |
81
|
30 |
|
if (!$other->containsKey($key)) { |
82
|
6 |
|
return false; |
83
|
|
|
} |
84
|
|
|
|
85
|
30 |
|
if (!$this->theseAreEqual($item, $other->get($key))) { |
86
|
30 |
|
return false; |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
|
90
|
21 |
|
return true; |
91
|
|
|
} |
92
|
|
|
|
93
|
3 |
|
public function values(): Vector |
94
|
|
|
{ |
95
|
3 |
|
$items = array_values($this->items); |
96
|
|
|
|
97
|
3 |
|
return new Vector($items); |
98
|
|
|
} |
99
|
|
|
|
100
|
3 |
|
public function keys(): Vector |
101
|
|
|
{ |
102
|
3 |
|
$items = array_keys($this->items); |
103
|
|
|
|
104
|
3 |
|
return new Vector($items); |
105
|
|
|
} |
106
|
|
|
|
107
|
12 |
|
public function add(string $key, $value): self |
108
|
|
|
{ |
109
|
12 |
|
if ($this->containsKey($key)) { |
110
|
3 |
|
throw InRangeException::alreadyContainsKey($key); |
111
|
|
|
} |
112
|
|
|
|
113
|
9 |
|
$items = $this->items; |
114
|
|
|
|
115
|
9 |
|
$items[$key] = $value; |
116
|
|
|
|
117
|
9 |
|
return new self($items); |
118
|
|
|
} |
119
|
|
|
|
120
|
12 |
View Code Duplication |
public function replace($searchValue, $replacementValue): self |
|
|
|
|
121
|
|
|
{ |
122
|
12 |
|
$items = $this->items; |
123
|
12 |
|
$key = $this->search($searchValue); |
124
|
|
|
|
125
|
9 |
|
$items[$key] = $replacementValue; |
126
|
|
|
|
127
|
9 |
|
return new self($items); |
128
|
|
|
} |
129
|
|
|
|
130
|
12 |
|
public function replaceAll($searchValue, $replacementValue): self |
131
|
|
|
{ |
132
|
12 |
|
$items = $this->items; |
133
|
12 |
|
$keys = $this->searchAll($searchValue); |
134
|
|
|
|
135
|
12 |
|
if ($keys->isEmpty()) { |
136
|
3 |
|
return $this; |
137
|
|
|
} |
138
|
|
|
|
139
|
9 |
|
foreach ($keys as $key) { |
140
|
9 |
|
$items[$key] = $replacementValue; |
141
|
|
|
} |
142
|
|
|
|
143
|
9 |
|
return new self($items); |
144
|
|
|
} |
145
|
|
|
|
146
|
9 |
View Code Duplication |
public function replaceKey(string $key, $replacementValue): self |
|
|
|
|
147
|
|
|
{ |
148
|
9 |
|
if (!$this->containsKey($key)) { |
149
|
3 |
|
throw OutOfRangeException::doesNotContainKey($key); |
150
|
|
|
} |
151
|
|
|
|
152
|
6 |
|
$items = $this->items; |
153
|
|
|
|
154
|
6 |
|
$items[$key] = $replacementValue; |
155
|
|
|
|
156
|
6 |
|
return new self($items); |
157
|
|
|
} |
158
|
|
|
|
159
|
12 |
View Code Duplication |
public function remove($value): self |
|
|
|
|
160
|
|
|
{ |
161
|
12 |
|
$items = $this->items; |
162
|
12 |
|
$key = $this->search($value); |
163
|
|
|
|
164
|
9 |
|
unset($items[$key]); |
165
|
|
|
|
166
|
9 |
|
return new self($items); |
167
|
|
|
} |
168
|
|
|
|
169
|
12 |
View Code Duplication |
public function removeAll($value): self |
|
|
|
|
170
|
|
|
{ |
171
|
12 |
|
$items = $this->items; |
172
|
12 |
|
$keys = $this->searchAll($value); |
173
|
|
|
|
174
|
12 |
|
if ($keys->isEmpty()) { |
175
|
3 |
|
return $this; |
176
|
|
|
} |
177
|
|
|
|
178
|
9 |
|
foreach ($keys as $key) { |
179
|
9 |
|
unset($items[$key]); |
180
|
|
|
} |
181
|
|
|
|
182
|
9 |
|
return new self($items); |
183
|
|
|
} |
184
|
|
|
|
185
|
9 |
View Code Duplication |
public function removeKey(string $key): self |
|
|
|
|
186
|
|
|
{ |
187
|
9 |
|
if (!$this->containsKey($key)) { |
188
|
3 |
|
throw OutOfRangeException::doesNotContainKey($key); |
189
|
|
|
} |
190
|
|
|
|
191
|
6 |
|
$items = $this->items; |
192
|
|
|
|
193
|
6 |
|
unset($items[$key]); |
194
|
|
|
|
195
|
6 |
|
return new self($items); |
196
|
|
|
} |
197
|
|
|
|
198
|
9 |
|
public function merge(self $other): self |
199
|
|
|
{ |
200
|
9 |
|
$items = array_merge($this->items, $other->items); |
201
|
|
|
|
202
|
9 |
|
return new self($items); |
203
|
|
|
} |
204
|
|
|
|
205
|
6 |
View Code Duplication |
public function intersect(self $other): self |
|
|
|
|
206
|
|
|
{ |
207
|
6 |
|
$items = []; |
208
|
|
|
|
209
|
6 |
|
foreach ($this->items as $key => $item) { |
210
|
6 |
|
if ($other->contains($item)) { |
211
|
6 |
|
$items[$key] = $item; |
212
|
|
|
} |
213
|
|
|
} |
214
|
|
|
|
215
|
6 |
|
return new self($items); |
216
|
|
|
} |
217
|
|
|
|
218
|
3 |
View Code Duplication |
public function intersectKeys(self $other): self |
|
|
|
|
219
|
|
|
{ |
220
|
3 |
|
$items = []; |
221
|
|
|
|
222
|
3 |
|
foreach ($this->items as $key => $item) { |
223
|
3 |
|
if ($other->containsKey($key)) { |
224
|
3 |
|
$items[$key] = $item; |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
228
|
3 |
|
return new self($items); |
229
|
|
|
} |
230
|
|
|
|
231
|
6 |
View Code Duplication |
public function diff(self $other): self |
|
|
|
|
232
|
|
|
{ |
233
|
6 |
|
$items = []; |
234
|
|
|
|
235
|
6 |
|
foreach ($this->items as $key => $item) { |
236
|
6 |
|
if (!$other->contains($item)) { |
237
|
6 |
|
$items[$key] = $item; |
238
|
|
|
} |
239
|
|
|
} |
240
|
|
|
|
241
|
6 |
|
return new self($items); |
242
|
|
|
} |
243
|
|
|
|
244
|
3 |
View Code Duplication |
public function diffKeys(self $other): self |
|
|
|
|
245
|
|
|
{ |
246
|
3 |
|
$items = []; |
247
|
|
|
|
248
|
3 |
|
foreach ($this->items as $key => $item) { |
249
|
3 |
|
if (!$other->containsKey($key)) { |
250
|
3 |
|
$items[$key] = $item; |
251
|
|
|
} |
252
|
|
|
} |
253
|
|
|
|
254
|
3 |
|
return new self($items); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* The filter callable is given an item and it's key, and should |
259
|
|
|
* return a boolean indicating whether the item remains or not. |
260
|
|
|
* |
261
|
|
|
* function ($item, $key): bool { |
262
|
|
|
* return true; |
263
|
|
|
* } |
264
|
|
|
*/ |
265
|
9 |
|
public function filter(callable $filter): self |
266
|
|
|
{ |
267
|
9 |
|
$items = array_filter($this->items, $filter, ARRAY_FILTER_USE_BOTH); |
268
|
|
|
|
269
|
9 |
|
return new self($items); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* The mapper callable is given an item, and should return |
274
|
|
|
* a new value to use in it's place. |
275
|
|
|
* |
276
|
|
|
* function ($item) { |
277
|
|
|
* return $item; |
278
|
|
|
* } |
279
|
|
|
*/ |
280
|
6 |
|
public function map(callable $mapper): self |
281
|
|
|
{ |
282
|
6 |
|
$items = array_map($mapper, $this->items); |
283
|
|
|
|
284
|
6 |
|
return new self($items); |
285
|
|
|
} |
286
|
|
|
|
287
|
255 |
|
private function guardAgainstInvalidKey($key) |
288
|
|
|
{ |
289
|
255 |
|
if (!is_string($key)) { |
290
|
3 |
|
throw InvalidArgumentException::invalidKeyInArray('values', 'string', $key); |
291
|
|
|
} |
292
|
252 |
|
} |
293
|
|
|
} |
294
|
|
|
|
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.