Passed
Push — version-4 ( 844ef0...ae0658 )
by Sebastian
02:51 queued 11s
created

ArrayListTrait   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 317
Duplicated Lines 0 %

Test Coverage

Coverage 81.97%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 97
c 1
b 0
f 0
dl 0
loc 317
ccs 100
cts 122
cp 0.8197
rs 8.4
wmc 50

27 Methods

Rating   Name   Duplication   Size   Complexity  
A map() 0 7 2
B contains() 0 22 9
A replace() 0 3 1
A subList() 0 9 3
A toArray() 0 3 1
A setArray() 0 3 1
A size() 0 3 1
A clear() 0 4 1
A shuffle() 0 5 1
A count() 0 3 1
A distinct() 0 23 5
A mapNotNull() 0 4 1
A any() 0 3 1
A remove() 0 3 1
A add() 0 4 1
A forEach() 0 4 2
A joinToString() 0 16 3
A all() 0 3 1
A collectToString() 0 3 1
A filter() 0 8 3
A getOrElse() 0 6 2
A chunk() 0 8 2
A searchBy() 0 4 1
A addAll() 0 4 2
A collect() 0 3 1
A isEmpty() 0 3 1
A flatten() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like ArrayListTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ArrayListTrait, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types=1);
3
/*
4
 * Copyright (C) 2018 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\Lists;
13
14
use Seboettg\Collection\Comparable\Comparable;
15
use Seboettg\Collection\Lists\ListFeatures\ListAccessTrait;
16
use Seboettg\Collection\Lists\ListFeatures\ListOperationsTrait;
17
use Seboettg\Collection\Lists\MapFeatures\MapFeaturesTrait;
18
use Seboettg\Collection\Map\MapInterface;
19
use Seboettg\Collection\NativePhp\IteratorTrait;
20
use function Seboettg\Collection\Assert\assertComparable;
21
use function Seboettg\Collection\Assert\assertStringable;
22
use function Seboettg\Collection\Map\mapOf;
23
use function Seboettg\Collection\Map\pair;
24
25
/**
26
 * @property array $array Base array of this data structure
27
 */
28
trait ArrayListTrait
29
{
30
    use ListAccessTrait,
31
        IteratorTrait,
32
        ListOperationsTrait,
33
        MapFeaturesTrait;
34
35
    /**
36
     * flush array list
37
     *
38
     * @return ListInterface|ArrayListTrait
39
     */
40 1
    public function clear(): void
41
    {
42 1
        unset($this->array);
43 1
        $this->array = [];
44 1
    }
45
46
    /**
47
     * Adds the specified element to the end of this list.
48
     *
49
     * @param mixed $element
50
     */
51 21
    public function add($element): void
52
    {
53 21
        end($this->array);
54 21
        $this->array[] = $element;
55 21
    }
56
57
    /**
58
     * @inheritDoc
59
     */
60 1
    public function addAll(iterable $elements): void
61
    {
62 1
        foreach ($elements as $element) {
63 1
            $this->add($element);
64
        }
65 1
    }
66
67
    /**
68
     * @inheritDoc
69
     */
70 1
    public function remove($key): void
71
    {
72 1
        unset($this->array[$key]);
73 1
    }
74
75
    /**
76
     * {@inheritdoc}
77
     */
78 8
    public function contains($value): bool
79
    {
80 8
        if ((isScalarOrStringable($value) && $this->all(fn($item) => isScalarOrStringable($item)))) {
81 5
            return in_array($value, $this->array) !== false;
82
        }
83 3
        if (isComparable($value) && $this->all(fn($item) => isComparable($item))) {
84 2
            $items = $this->array;
85
            /** @var Comparable $value */
86
            /** @var Comparable $item */
87 2
            foreach ($items as $item) {
88 2
                if ($item->compareTo($value) === 0) {
89 2
                    return true;
90
                }
91
            }
92
        } else {
93 1
            if ($value instanceof ListInterface && $this->all(fn($item) => $item instanceof ListInterface)) {
94 1
                return in_array(print_r($value->toArray(), true), array_map(fn ($item) => print_r($item->toArray(), true), $this->array)) !== false;
95
            } else {
96
                return in_array(spl_object_hash($value), array_map(fn($item) => spl_object_hash($item), $this->array));
97
            }
98
        }
99 2
        return false;
100
    }
101
102
    /**
103
     * @inheritDoc
104
     */
105 1
    public function shuffle(): ListInterface
106
    {
107 1
        $array = $this->array;
108 1
        shuffle($array);
109 1
        return listFromArray($array);
110
    }
111
112
    /**
113
     * @inheritDoc
114
     */
115 16
    public function filter(?callable $predicate = null, bool $preserveKeys = false): ListInterface
116
    {
117 16
        $list = emptyList();
118 16
        $filtered = $predicate == null ? array_filter($this->array) : array_filter($this->array, $predicate);
119 16
        $list->setArray(
120 16
            $preserveKeys ? $filtered : array_values($filtered)
121
        );
122 16
        return $list;
123
    }
124
125
    /**
126
     * @param array $array
127
     */
128 45
    public function setArray(array $array): void
129
    {
130 45
        $this->replace($array);
131 45
    }
132
133
    /**
134
     * @param array $data
135
     */
136 45
    public function replace(array $data): void
137
    {
138 45
        $this->array = $data;
139 45
    }
140
141
    /**
142
     * returns a new ArrayList containing all the elements of this ArrayList after applying the callback function to each one.
143
     * @param callable $mapFunction
144
     * @return ListInterface|ArrayListTrait
145
     */
146 7
    public function map(callable $mapFunction): ListInterface
147
    {
148 7
        $list = emptyList();
149 7
        foreach ($this as $value) {
150 7
            $list->add($mapFunction($value));
151
        }
152 6
        return $list;
153
    }
154
155
    /**
156
     * @inheritDoc
157
     * @param callable $mapFunction
158
     * @return ListInterface
159
     */
160 1
    public function mapNotNull(callable $mapFunction): ListInterface
161
    {
162 1
        $list = $this->map($mapFunction);
163 1
        return $list->filter();
164
    }
165
166
    /**
167
     * Returns a new ArrayList containing a one-dimensional array of all elements of this ArrayList. Keys are going lost.
168
     * @return ListInterface
169
     */
170 1
    public function flatten(): ListInterface
171
    {
172 1
        $flattenedArray = [];
173
        array_walk_recursive($this->array, function ($item) use (&$flattenedArray) {
174 1
            $flattenedArray[] = $item;
175 1
        });
176 1
        return listOf(...$flattenedArray);
177
    }
178
179
    /**
180
     * @inheritDoc
181
     * @param callable $collectFunction
182
     * @return mixed
183
     */
184 1
    public function collect(callable $collectFunction)
185
    {
186 1
        return $collectFunction($this->array);
187
    }
188
189
    /**
190
     * @inheritDoc
191
     */
192 4
    public function joinToString(string $delimiter, string $prefix = null, string $suffix = null): string
193
    {
194
        $result = implode($delimiter, $this->map(function ($item) {
195 4
            assertStringable(
196 4
                $item,
197 4
                "All elements in the list must be convertible to string in order to use joinToString."
198
            );
199 3
            return strval($item);
200 4
        })->toArray());
201 3
        if ($prefix !== null) {
202
            $result = $prefix . $result;
203
        }
204 3
        if ($suffix !== null) {
205
            $result = $result . $suffix;
206
        }
207 3
        return $result;
208
    }
209
210
    /**
211
     * @inheritDoc
212
     * @deprecated use joinToString instead
213
     */
214 1
    public function collectToString(string $delimiter): string
215
    {
216 1
        return $this->joinToString($delimiter);
217
    }
218
219
    /**
220
     * @inheritDoc
221
     * @return int
222
     */
223 21
    public function count(): int
224
    {
225 21
        return count($this->array);
226
    }
227
228
    /**
229
     * @inheritDoc
230
     */
231
    public function size(): int
232
    {
233
        return $this->count();
234
    }
235
236 2
    public function any(callable $predicate): bool
237
    {
238 2
        return $this->filter($predicate)->count() > 0;
239
    }
240
241 10
    public function all(callable $predicate): bool
242
    {
243 10
        return $this->count() === $this->filter($predicate)->count();
244
    }
245
246
    /**
247
     * @inheritDoc
248
     */
249 2
    public function chunk(int $size): ListInterface
250
    {
251 2
        $listOfChunks = emptyList();
252 2
        $arrayChunks = array_chunk($this->array, $size);
253 2
        foreach ($arrayChunks as $arrayChunk) {
254 2
            $listOfChunks->add(listOf(...$arrayChunk));
255
        }
256 2
        return $listOfChunks;
257
    }
258
259 1
    public function distinct(): ListInterface
260
    {
261 1
        $this->forEach(fn($item) => assertComparable($item,
262 1
            sprintf(
263 1
                "Each item must be of type scalar or implement \Stringable or implement %s",
264 1
                Comparable::class
265
            )
266 1
        ));
267 1
        $newList = emptyList();
268 1
        if ($this->all(fn($item): bool => isScalarOrStringable($item))) {
269
            return listFromArray(array_unique($this->toArray()));
270
        } else {
271 1
            if ($this->all(fn($item): bool => isComparable($item))) {
272 1
                $values = $this->array;
273 1
                foreach ($values as $value) {
274 1
                    if (!$newList->contains($value)) {
275 1
                        $newList->add($value);
276
                    }
277
                }
278 1
                return $newList;
279
            }
280
        }
281
        return listFromArray($this->array);
282
    }
283
284
    /**
285
     * @inheritDoc
286
     */
287 7
    public function forEach(callable $action): void
288
    {
289 7
        foreach ($this->array as $element) {
290 7
            $action($element);
291
        }
292 7
    }
293
294
    /**
295
     * @inheritDoc
296
     */
297
    public function getOrElse(int $index, callable $defaultValue)
298
    {
299
        if ($this->array[$index] !== null) {
300
            return $this->array[$index];
301
        }
302
        return $defaultValue();
303
    }
304
305
    /**
306
     * @inheritDoc
307
     * @param int $fromIndex
308
     * @param int $toIndex
309
     * @return ListInterface
310
     */
311
    public function subList(int $fromIndex, int $toIndex): ListInterface
312
    {
313
        $list = emptyList();
314
        for ($i = $fromIndex; $i < $toIndex; ++$i) {
315
            if (isset($this->array[$i])) {
316
                $list->add($this->array[$i]);
317
            }
318
        }
319
        return $list;
320
    }
321
322
    /**
323
     * Return first element of this list that matches the matchingCondition
324
     *
325
     * @param callable $matchingCondition
326
     * @return mixed|null
327
     */
328
    public function searchBy(callable $matchingCondition)
329
    {
330
        $list = listOf(...array_filter($this->array));
331
        return $list->filter($matchingCondition)->first();
332
    }
333
334
    public function isEmpty(): bool
335
    {
336
        return $this->count() === 0;
337
    }
338
339
    /**
340
     * {@inheritDoc}
341
     */
342 10
    public function toArray(): array
343
    {
344 10
        return $this->array;
345
    }
346
}
347