Issues (35)

src/Map/MapTrait.php (4 issues)

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
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 21
    public function count(): int
67
    {
68 21
        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 50
    public function setArray(array $array): void
104
    {
105 50
        $this->array = $array;
106 50
    }
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 3
    public function isEmpty(): bool
145
    {
146 3
        return $this->count() === 0;
147
    }
148
149
    /**
150
     * @inheritDoc
151
     * @return bool
152
     */
153 1
    public function isNotEmpty(): bool
154
    {
155 1
        return !$this->isEmpty();
156
    }
157
158
    /**
159
     * @inheritDoc
160
     * @param scalar
161
     * @return mixed
162
     */
163 6
    public function get($key)
164
    {
165 6
        return $this->array[$key] ?? null;
166
    }
167
168
    /**
169
     * @inheritDoc
170
     * @param scalar $key
171
     * @param mixed $value
172
     */
173 9
    public function put($key, $value): void
174
    {
175 9
        assertScalar($key, "Key must be a scalar value");
176 8
        $this->array[$key] = $value;
177 8
    }
178
179
    /**
180
     * @param MapInterface<scalar, mixed> $map
181
     * @return void
182
     */
183 1
    public function putAll(MapInterface $map): void
184
    {
185 1
        foreach ($map as $key => $value) {
186 1
            $this->array[$key] = $value;
187
        }
188 1
    }
189
190
    /**
191
     * @inheritDoc
192
     * @param scalar $key
193
     * @return void
194
     */
195 1
    public function remove($key): void
196
    {
197 1
        unset($this->array[$key]);
198 1
    }
199
200
    /**
201
     * @inheritDoc
202
     * @param callable $predicate
203
     * @return bool
204
     */
205 1
    public function all(callable $predicate): bool
206
    {
207 1
        return $this->count() === $this->filter($predicate)->count();
208
    }
209
210
    /**
211
     * @inheritDoc
212
     * @param callable $predicate
213
     * @return bool
214
     */
215 1
    public function any(callable $predicate): bool
216
    {
217 1
        return $this->filter($predicate)->count() > 0;
218
    }
219
220
    /**
221
     * @inheritDoc
222
     * @param callable|null $predicate
223
     * @return MapInterface
224
     */
225 7
    public function filter(callable $predicate = null): MapInterface
226
    {
227 7
        $map = emptyMap();
228 7
        if ($predicate !== null) {
229
            try {
230 6
                $reflected = new ReflectionFunction($predicate);
231 6
                if (count($reflected->getParameters()) === 1) {
232 3
                    assertValidCallable($predicate, [Pair::class]);
233 3
                    foreach ($this->array as $key => $value) {
234 3
                        if ($predicate(pair($key, $value)) === true) {
235 3
                            $map->put($key, $value);
236
                        }
237
                    }
238
                } else {
239 3
                    if (count($reflected->getParameters()) === 2) {
240 3
                        assertValidCallable($predicate, ["scalar", "mixed"]);
241
                    }
242 5
                    foreach ($this->array as $key => $value) {
243 2
                        if ($predicate($key, $value) === true) {
244 2
                            $map->put($key, $value);
245
                        }
246
                    }
247
                }
248 1
            } catch (ReflectionException $ex) {
249 5
                throw new NotApplicableCallableException("Invalid callable passed.");
250
            }
251
        } else {
252 1
            $map->array = array_filter($this->array);
0 ignored issues
show
The property array is declared private in anonymous//src/Map/Functions.php$0 and cannot be accessed from this context.
Loading history...
253
        }
254 6
        return $map;
255
    }
256
257
    /**
258
     * @inheritDoc
259
     * @param scalar $key
260
     * @param callable $default
261
     * @return mixed
262
     */
263 2
    public function getOrElse($key, callable $default)
264
    {
265 2
        return $this[$key] ?? $default($this);
266
    }
267
268
    /**
269
     * @inheritDoc
270
     * @param callable $transform
271
     * @return ListInterface
272
     */
273 3
    public function map(callable $transform): ListInterface
274
    {
275 3
        $list = emptyList();
276
        try {
277 3
            $reflected = new ReflectionFunction($transform);
278 3
            if (count($reflected->getParameters()) === 1) {
279 2
                assertValidCallable($transform, [Pair::class]);
280 2
                foreach ($this->array as $key => $value) {
281 2
                    $list->add($transform(pair($key, $value)));
282
                }
283
            } else {
284 1
                if (count($reflected->getParameters()) === 2) {
285 1
                    assertValidCallable($transform, ["scalar", "mixed"]);
286
                }
287 3
                foreach ($this->array as $key => $value) {
288 1
                    $list->add($transform($key, $value));
289
                }
290
            }
291
        } catch (ReflectionException $ex) {
292
            throw new NotApplicableCallableException("Invalid callable passed.");
293
        }
294 3
        return $list;
295
    }
296
297
    /**
298
     * @inheritDoc
299
     * @param callable $transform
300
     * @return ListInterface
301
     */
302 1
    public function mapNotNull(callable $transform): ListInterface
303
    {
304 1
        return $this->map($transform)->filter(fn($item) => $item !== null);
305
    }
306
307
    /**
308
     * @inheritDoc
309
     * @param iterable<scalar> $keys
310
     * @param MapInterface
311
     */
312 2
    public function minus(iterable $keys): MapInterface
313
    {
314 2
        $newInstance = emptyMap();
315 2
        foreach ($this->array as $key => $value) {
316 2
            if (!$this->iterableContainsKey($key, $keys)) {
317 2
                $newInstance[$key] = $value;
318
            }
319
        }
320 2
        return $newInstance;
321
    }
322
323 2
    private function iterableContainsKey($key, iterable $keys): bool
324
    {
325 2
        foreach ($keys as $k) {
326 2
            if ($k === $key) {
327 2
                return true;
328
            }
329
        }
330 2
        return false;
331
    }
332
333
    /**
334
     * @inheritDoc
335
     * @param iterable<Pair<scalar, mixed>> $pairs
336
     * @return MapInterface<scalar, mixed>
337
     */
338 2
    public function plus(iterable $pairs): MapInterface
339
    {
340 2
        $map = emptyMap();
341 2
        $map->array = $this->array;
0 ignored issues
show
The property array is declared private in anonymous//src/Map/Functions.php$0 and cannot be accessed from this context.
Loading history...
342 2
        if ($pairs instanceof MapInterface) {
343 1
            foreach ($pairs as $key => $value) {
344 1
                $map[$key] = $value;
345
            }
346
        } else {
347 1
            foreach ($pairs as $pair) {
348 1
                assertType($pair, Pair::class,
349 1
                    sprintf(
350 1
                        "Expected object of type %s, object of type %s given",
351 1
                        Pair::class,
352 1
                        gettype($pair) === "object" ? get_class($pair) : gettype($pair)
353
                    )
354
                );
355 1
                $map[$pair->getKey()] = $pair->getValue();
356
            }
357
        }
358 2
        return $map;
359
    }
360
361
    /**
362
     * @inheritDoc
363
     * @param callable $action f(entry: Pair<scalar, mixed>) -> mixed|void OR f(key: scalar, value: mixed>) -> mixed|void
364
     * @return void
365
     */
366 2
    public function forEach(callable $action): void
367
    {
368
        try {
369 2
            $reflected = new ReflectionFunction($action);
370 2
            if (count($reflected->getParameters()) === 1) {
371 2
                assertValidCallable($action, [Pair::class]);
372 2
                foreach ($this->array as $key => $value) {
373 2
                    $action(pair($key, $value));
374
                }
375
            } else {
376 1
                if (count($reflected->getParameters()) === 2) {
377 1
                    assertValidCallable($action, ["scalar", "mixed"]);
378
                }
379 2
                foreach ($this->array as $key => $value) {
380 1
                    $action($key, $value);
381
                }
382
            }
383
        } catch (ReflectionException $ex) {
384
            throw new NotApplicableCallableException("Invalid callable passed.");
385
        }
386 2
    }
387
388
    /**
389
     * @inheritDoc
390
     * @return ListInterface<Pair<scalar, mixed>>
391
     */
392 2
    public function toList(): ListInterface
393
    {
394 2
        $list = emptyList();
395 2
        foreach ($this->array as $key => $value) {
396 2
            $list->add(pair($key, $value));
397
        }
398 2
        return $list;
399
    }
400
401 1
    public function toMap(): MapInterface
402
    {
403 1
        return mapOf(...$this->toList());
404
    }
405
406
    /**
407
     * {@inheritDoc}
408
     */
409 1
    public function toArray(): array
410
    {
411 1
        return $this->array;
412
    }
413
}
414