Passed
Push — master ( 97a669...e605bd )
by Sebastian
07:55
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 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
        try {
268 2
            $reflected = new ReflectionFunction($transform);
269 2
            if (count($reflected->getParameters()) === 1) {
270 2
                assertValidCallable($transform, [Pair::class]);
271 2
                foreach ($this->array as $key => $value) {
272 2
                    $list->add($transform(pair($key, $value)));
273
                }
274
            } else {
275
                if (count($reflected->getParameters()) === 2) {
276
                    assertValidCallable($transform, ["scalar", "mixed"]);
277
                }
278 2
                foreach ($this->array as $key => $value) {
279
                    $list->add($transform($key, $value));
280
                }
281
            }
282
        } catch (ReflectionException $ex) {
283
            throw new NotApplicableCallableException("Invalid callable passed.");
284
        }
285 2
        return $list;
286
    }
287
288
    /**
289
     * @inheritDoc
290
     * @param callable $transform
291
     * @return ListInterface
292
     */
293 1
    public function mapNotNull(callable $transform): ListInterface
294
    {
295 1
        return $this->map($transform)->filter(fn($item) => $item !== null);
296
    }
297
298
    /**
299
     * @inheritDoc
300
     * @param iterable<scalar> $keys
301
     * @param MapInterface
302
     */
303 2
    public function minus(iterable $keys): MapInterface
304
    {
305 2
        $newInstance = emptyMap();
306 2
        foreach ($this->array as $key => $value) {
307 2
            if (!$this->iterableContainsKey($key, $keys)) {
308 2
                $newInstance[$key] = $value;
309
            }
310
        }
311 2
        return $newInstance;
312
    }
313
314 2
    private function iterableContainsKey($key, iterable $keys): bool
315
    {
316 2
        foreach ($keys as $k) {
317 2
            if ($k === $key) {
318 2
                return true;
319
            }
320
        }
321 2
        return false;
322
    }
323
324
    /**
325
     * @inheritDoc
326
     * @param iterable<Pair<scalar, mixed>> $pairs
327
     * @return MapInterface<scalar, mixed>
328
     */
329 2
    public function plus(iterable $pairs): MapInterface
330
    {
331 2
        $map = emptyMap();
332 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...
333 2
        if ($pairs instanceof MapInterface) {
334 1
            foreach ($pairs as $key => $value) {
335 1
                $map[$key] = $value;
336
            }
337
        } else {
338 1
            foreach ($pairs as $pair) {
339 1
                assertType($pair, Pair::class,
340 1
                    sprintf(
341 1
                        "Expected object of type %s, object of type %s given",
342 1
                        Pair::class,
343 1
                        gettype($pair) === "object" ? get_class($pair) : gettype($pair)
344
                    )
345
                );
346 1
                $map[$pair->getKey()] = $pair->getValue();
347
            }
348
        }
349 2
        return $map;
350
    }
351
352
    /**
353
     * @inheritDoc
354
     * @param callable $action f(entry: Pair<scalar, mixed>) -> mixed|void
355
     * @return void
356
     */
357 2
    public function forEach(callable $action): void
358
    {
359 2
        foreach ($this->array as $key => $value) {
360 2
            $action(pair($key, $value));
361
        }
362 2
    }
363
364
    /**
365
     * @inheritDoc
366
     * @return ListInterface<Pair<scalar, mixed>>
367
     */
368 2
    public function toList(): ListInterface
369
    {
370 2
        $list = emptyList();
371 2
        foreach ($this->array as $key => $value) {
372 2
            $list->add(pair($key, $value));
373
        }
374 2
        return $list;
375
    }
376
377 1
    public function toMap(): MapInterface
378
    {
379 1
        return mapOf(...$this->toList());
380
    }
381
382
    /**
383
     * {@inheritDoc}
384
     */
385 1
    public function toArray(): array
386
    {
387 1
        return $this->array;
388
    }
389
}
390