Passed
Push — version-4 ( c08a3b...84aff8 )
by Sebastian
08:36
created

MapTrait::plus()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 14
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 21
ccs 14
cts 14
cp 1
crap 5
rs 9.4888
1
<?php
2
declare(strict_types=1);
3
/*
4
 * Copyright (C) 2022 Sebastian Böttger <[email protected]>
5
 * You may use, distribute and modify this code under the
6
 * terms of the MIT license.
7
 *
8
 * You should have received a copy of the MIT license with
9
 * this file. If not, please visit: https://opensource.org/licenses/mit-license.php
10
 */
11
12
namespace Seboettg\Collection\Map;
13
14
use ArrayIterator;
15
use Iterator;
16
use ReflectionException;
17
use ReflectionFunction;
18
use Seboettg\Collection\Assert\Exception\NotApplicableCallableException;
19
use Seboettg\Collection\Lists;
20
use Seboettg\Collection\Lists\ArrayListTrait;
21
use Seboettg\Collection\Lists\ListInterface;
22
use Seboettg\Collection\Map;
23
use Seboettg\Collection\NativePhp\ArrayAccessTrait;
24
use function Seboettg\Collection\Assert\assertScalar;
25
use function Seboettg\Collection\Assert\assertType;
26
use function Seboettg\Collection\Assert\assertValidCallable;
27
use function Seboettg\Collection\Lists\emptyList;
28
use function Seboettg\Collection\Lists\listOf;
29
30
/**
31
 * @property array $array base array of this data structure
32
 */
33
trait MapTrait
34
{
35
    use ArrayAccessTrait;
36
37
    /**
38
     * @inheritDoc
39
     */
40 1
    public function getEntries(): ListInterface
41
    {
42
        return listOf(...array_map(function (string $key, $value) {
43 1
            return pair($key, $value);
44 1
        }, array_keys($this->array), $this->array));
45
46
    }
0 ignored issues
show
Coding Style introduced by
Function closing brace must go on the next line following the body; found 1 blank lines before brace
Loading history...
47
48
    /**
49
     * @inheritDoc
50
     */
51 2
    public function getKeys(): ListInterface
52
    {
53 2
        return listOf(...array_keys($this->array));
54
    }
55
56
    /**
57
     * @inheritDoc
58
     */
59 2
    public function values(): ListInterface
60
    {
61 2
        return listOf(...array_values($this->array));
62
    }
63
64
    /**
65
     * {@inheritDoc}
66
     */
67 16
    public function count(): int
68
    {
69 16
        return count($this->array);
70
    }
71
72
    /**
73
     * @inheritDoc
74
     */
75 1
    public function size(): int
76
    {
77 1
        return $this->count();
78
    }
79
80
    /**
81
     * @inheritDoc
82
     */
83
    public function clear(): void
84
    {
85
        unset($this->array);
86
        $this->array = [];
87
    }
88
89
    /**
90
     * @inheritDoc
91
     * @param scalar $key
92
     * @return bool
93
     */
94 6
    public function contains($key): bool
95
    {
96 6
        assertScalar($key, "Key must be a scalar value");
97 4
        return array_key_exists($key, $this->array);
98
    }
99
100
    /**
101
     * @inheritDoc
102
     * @param array $array
103
     */
104
    public function setArray(array $array): void
105
    {
106
        $this->array = $array;
107
    }
108
109
    /**
110
     * @inheritDoc
111
     * @param scalar
112
     * @return string
113
     */
114 1
    public function containsKey($key): bool
115
    {
116 1
        return $this->contains($key);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->contains($key) returns the type boolean which is incompatible with the documented return type string.
Loading history...
117
    }
118
119
    /**
120
     * @inheritDoc
121
     * @param mixed $value
122
     * @return bool
123
     */
124
    public function containsValue($value): bool
125
    {
126
        return in_array($value, $this->array, true) !== false;
127
    }
128
129
    /**
130
     * @inheritDoc
131
     * @return bool
132
     */
133 2
    public function isEmpty(): bool
134
    {
135 2
        return $this->count() === 0;
136
    }
137
138
    /**
139
     * @inheritDoc
140
     * @param scalar
141
     * @return mixed
142
     */
143 5
    public function get($key)
144
    {
145 5
        return $this->array[$key] ?? null;
146
    }
147
148
    /**
149
     * @inheritDoc
150
     * @param scalar $key
151
     * @param mixed $value
152
     */
153 36
    public function put($key, $value): void
154
    {
155 36
        assertScalar($key, "Key must be a scalar value");
156 36
        $this->array[$key] = $value;
157 36
    }
158
159
    /**
160
     * @param MapInterface<scalar, mixed> $map
161
     * @return void
162
     */
163 1
    public function putAll(MapInterface $map): void
164
    {
165 1
        foreach ($map as $key => $value) {
166 1
            $this->array[$key] = $value;
167
        }
168 1
    }
169
170
    /**
171
     * @inheritDoc
172
     * @param scalar $key
173
     * @return void
174
     */
175 1
    public function remove($key): void
176
    {
177 1
        unset($this->array[$key]);
178 1
    }
179
180
    /**
181
     * @inheritDoc
182
     * @param callable $predicate
183
     * @return bool
184
     */
185 1
    public function all(callable $predicate): bool
186
    {
187 1
        return $this->count() === $this->filter($predicate)->count();
188
    }
189
190
    /**
191
     * @inheritDoc
192
     * @param callable $predicate
193
     * @return bool
194
     */
195 1
    public function any(callable $predicate): bool
196
    {
197 1
        return $this->filter($predicate)->count() > 0;
198
    }
199
200
    /**
201
     * @inheritDoc
202
     * @param callable|null $predicate
203
     * @return MapInterface
204
     */
205 5
    public function filter(callable $predicate = null): MapInterface
206
    {
207 5
        $map = emptyMap();
208 5
        if ($predicate !== null) {
209
            try {
210 5
                $reflected = new ReflectionFunction($predicate);
211 5
                if (count($reflected->getParameters()) === 1) {
212 3
                    assertValidCallable($predicate, [Pair::class]);
213 3
                    foreach ($this->array as $key => $value) {
214 3
                        if ($predicate(pair($key, $value)) === true) {
215 3
                            $map->put($key, $value);
216
                        }
217
                    }
218
                } else {
219 2
                    if (count($reflected->getParameters()) === 2) {
220 2
                        assertValidCallable($predicate, ["scalar", "mixed"]);
221
                    }
222 5
                    foreach ($this->array as $key => $value) {
223 2
                        if ($predicate($key, $value) === true) {
224 2
                            $map->put($key, $value);
225
                        }
226
                    }
227
                }
228
            } catch (ReflectionException $ex) {
229 5
                throw new NotApplicableCallableException("Invalid callback.");
230
            }
231
        } else {
232
            $map->array = array_filter($this->array, $predicate);
0 ignored issues
show
Bug introduced by
The property array is declared private in anonymous//src/Map/Functions.php$0 and cannot be accessed from this context.
Loading history...
233
        }
234 5
        return $map;
235
    }
236
237
    /**
238
     * @inheritDoc
239
     * @param scalar $key
240
     * @param callable $default
241
     * @return mixed
242
     */
243 2
    public function getOrElse($key, callable $default)
244
    {
245 2
        return $this[$key] ?? $default($this);
246
    }
247
248
    /**
249
     * @inheritDoc
250
     * @param callable $transform
251
     * @return ListInterface
252
     */
253 2
    public function map(callable $transform): ListInterface
254
    {
255 2
        $list = emptyList();
256 2
        foreach ($this->array as $key => $value) {
257 2
            $list->add($transform(pair($key, $value)));
258
        }
259 2
        return $list;
260
    }
261
262
    /**
263
     * @inheritDoc
264
     * @param callable $transform
265
     * @return ListInterface
266
     */
267 1
    public function mapNotNull(callable $transform): ListInterface
268
    {
269 1
        return $this->map($transform)->filter(fn($item) => $item !== null);
270
    }
271
272
    /**
273
     * @inheritDoc
274
     * @param iterable<scalar> $keys
275
     * @param MapInterface
276
     */
277 2
    public function minus(iterable $keys): MapInterface
278
    {
279 2
        $newInstance = emptyMap();
280 2
        foreach ($this->array as $key => $value) {
281 2
            if (!$this->iterableContainsKey($key, $keys)) {
282 2
                $newInstance[$key] = $value;
283
            }
284
        }
285 2
        return $newInstance;
286
    }
287
288 2
    private function iterableContainsKey($key, iterable $keys): bool
289
    {
290 2
        foreach ($keys as $k) {
291 2
            if ($k === $key) {
292 2
                return true;
293
            }
294
        }
295 2
        return false;
296
    }
297
298
    /**
299
     * @inheritDoc
300
     * @param iterable<Pair<scalar, mixed>> $pairs
301
     * @return MapInterface<scalar, mixed>
302
     */
303 2
    public function plus(iterable $pairs): MapInterface
304
    {
305 2
        $map = emptyMap();
306 2
        $map->array = $this->array;
0 ignored issues
show
Bug introduced by
The property array is declared private in anonymous//src/Map/Functions.php$0 and cannot be accessed from this context.
Loading history...
307 2
        if ($pairs instanceof MapInterface) {
308 1
            foreach ($pairs as $key => $value) {
309 1
                $map[$key] = $value;
310
            }
311
        } else {
312 1
            foreach ($pairs as $pair) {
313 1
                assertType($pair, Pair::class,
314 1
                    sprintf(
315 1
                        "Expected object of type %s, object of type %s given",
316 1
                        Pair::class,
317 1
                        gettype($pair) === "object" ? get_class($pair) : gettype($pair)
318
                    )
319
                );
320 1
                $map[$pair->getKey()] = $pair->getValue();
321
            }
322
        }
323 2
        return $map;
324
    }
325
326
    /**
327
     * @inheritDoc
328
     * @param callable $action f(entry: Pair<scalar, mixed>) -> mixed|void
329
     * @return void
330
     */
331 1
    public function forEach(callable $action): void
332
    {
333 1
        foreach ($this->array as $key => $value) {
334 1
            $action(pair($key, $value));
335
        }
336 1
    }
337
338
    /**
339
     * @inheritDoc
340
     * @return ListInterface<Pair<scalar, mixed>>
341
     */
342 2
    public function toList(): ListInterface
343
    {
344 2
        $list = emptyList();
345 2
        foreach ($this->array as $key => $value) {
346 2
            $list->add(pair($key, $value));
347
        }
348 2
        return $list;
349
    }
350
351 1
    public function toMap(): MapInterface
352
    {
353 1
        return mapOf(...$this->toList());
354
    }
355
356
357 2
    public function getIterator(): Iterator
358
    {
359 2
        return new ArrayIterator($this->array);
360
    }
361
}
362