Passed
Branch master (fb2e38)
by Sebastian
02:23
created

MapTrait::filter()   B

Complexity

Conditions 9
Paths 15

Size

Total Lines 30
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 9

Importance

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