Completed
Push — master ( f73305...6db18d )
by Nate
02:26
created

ArrayList::assertIndex()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 3
nc 2
nop 1
crap 12
1
<?php
2
/*
3
 * Copyright (c) Nate Brunette.
4
 * Distributed under the MIT License (http://opensource.org/licenses/MIT)
5
 */
6
7
namespace Tebru\Collection;
8
9
use ArrayIterator;
10
use OutOfBoundsException;
11
12
/**
13
 * Class ArrayList
14
 *
15
 * Implements [@see ListInterface] and maintains an array of elements
16
 *
17
 * @author Nate Brunette <[email protected]>
18
 */
19
class ArrayList extends AbstractList
20
{
21
    /**
22
     * An array of elements
23
     *
24
     * @var array
25
     */
26
    protected $elements = [];
27
28
    /**
29
     * Constructor
30
     *
31
     * @param array $elements
32
     */
33 34
    public function __construct(array $elements = [])
34
    {
35 34
        $this->elements = array_values($elements);
36 34
    }
37
38
    /**
39
     * Ensure the element exists in the collection
40
     *
41
     * Returns true if the collection can contain duplicates,
42
     * and false if it cannot.
43
     *
44
     * @param mixed $element
45
     * @return bool
46
     */
47 31
    public function add($element): bool
48
    {
49 31
        $this->elements[] = $element;
50
51 31
        return true;
52
    }
53
54
    /**
55
     * Removes object if it exists
56
     *
57
     * By default this method will use strict comparison checking, passing false
58
     * in will use a double equals (==) instead.
59
     *
60
     * Returns true if the element was removed
61
     *
62
     * @param mixed $element
63
     * @param bool $strict
64
     * @return bool
65
     */
66 4
    public function remove($element, bool $strict = true): bool
67
    {
68 4
        $index = $this->indexOf($element, $strict);
69
70 4
        if (-1 === $index) {
71 1
            return false;
72
        }
73
74 3
        array_splice($this->elements, $index, 1);
75
76 3
        return true;
77
    }
78
79
    /**
80
     * Removes all elements from a collection
81
     *
82
     * @return void
83
     */
84 1
    public function clear()
85
    {
86 1
        $this->elements = [];
87 1
    }
88
89
    /**
90
     * Returns the size of the collection
91
     *
92
     * @return int
93
     */
94 30
    public function count(): int
95
    {
96 30
        return count($this->elements);
97
    }
98
99
    /**
100
     * Returns true if the collection contains element
101
     *
102
     * @param mixed $element
103
     * @param bool $strict
104
     * @return bool
105
     */
106 2
    public function contains($element, bool $strict = true): bool
107
    {
108 2
        return in_array($element, $this->elements, $strict);
109
    }
110
111
    /**
112
     * Returns an array of all elements in the collection
113
     *
114
     * @return array
115
     */
116 13
    public function toArray(): array
117
    {
118 13
        return $this->elements;
119
    }
120
121
    /**
122
     * Filter the collection using closure
123
     *
124
     * The closure will get passed each element.  Returning true from the
125
     * closure will include that element in the new collection.
126
     *
127
     * @param callable $filter
128
     * @return CollectionInterface
129
     */
130 1
    public function filter(callable $filter): CollectionInterface
131
    {
132 1
        return new ArrayList(array_filter($this->elements, $filter));
133
    }
134
135
    /**
136
     * Retrieve an external iterator
137
     *
138
     * @return ArrayIterator
139
     */
140 25
    public function getIterator(): ArrayIterator
141
    {
142 25
        return new ArrayIterator($this->elements);
143
    }
144
145
    /**
146
     * Insert element at the specified index
147
     *
148
     * @param int $index
149
     * @param mixed $element
150
     * @return void
151
     * @throws \OutOfBoundsException if the index doesn't exist
152
     */
153 6
    public function insert(int $index, $element)
154
    {
155 6
        $this->assertIndex($index);
156 1
157
        array_splice($this->elements, $index, 0, [$element]);
158
    }
159 5
160 5
    /**
161
     * Inserts all elements of a collection at index
162
     *
163
     * @param int $index
164
     * @param CollectionInterface $collection
165
     * @return bool
166
     * @throws \OutOfBoundsException if the index doesn't exist
167
     */
168
    public function insertAll(int $index, CollectionInterface $collection): bool
169
    {
170 4
        $this->assertIndex($index);
171
172 4
        $size = $this->count();
173 1
        array_splice($this->elements, $index, 0, $collection->toArray());
174
175
        return $size !== $this->count();
176 3
    }
177 3
178
    /**
179 3
     * Returns the element at the index
180
     *
181
     * @param int $index
182
     * @return mixed
183
     * @throws \OutOfBoundsException if the index doesn't exist
184
     */
185
    public function get(int $index)
186
    {
187
        if (!$this->has($index)) {
188
            throw new OutOfBoundsException(sprintf('Tried to access element at index "%s"', $index));
189 13
        }
190
191 13
        return $this->elements[$index];
192 2
    }
193
194
    /**
195 11
     * Returns true if an element exists at the index
196
     *
197
     * @param int $index
198
     * @return bool
199
     */
200
    public function has(int $index): bool
201
    {
202
        return array_key_exists($index, $this->elements);
203
    }
204 19
205
    /**
206 19
     * Returns the index of the first instance of the element, -1 if the element
207
     * doesn't exist
208
     *
209
     * By default this method will use strict comparison checking, passing false
210
     * in will use a double equals (==) instead.
211
     *
212
     * @param mixed $element
213
     * @param bool $strict
214
     * @return int
215
     */
216
    public function indexOf($element, bool $strict = true): int
217
    {
218
        $index = array_search($element, $this->elements, $strict);
219
220 7
        return false === $index ? -1 : $index;
221
    }
222 7
223
    /**
224 7
     * Returns the index of the last instance of the element, -1 if the element
225
     * doesn't exist
226
     *
227
     * By default this method will use strict comparison checking, passing false
228
     * in will use a double equals (==) instead.
229
     *
230
     * @param mixed $element
231
     * @param bool $strict
232
     * @return int
233
     */
234
    public function lastIndexOf($element, bool $strict = true): int
235
    {
236
        $elements = array_reverse($this->elements);
237
238 3
        $index = array_search($element, $elements, $strict);
239
240 3
        // return -1 if not found or (size - index found - 1) to return the index of the element after reverse
241
        return false === $index ? -1 : $this->count() - $index - 1;
242 3
    }
243
244
    /**
245 3
     * Removes the element at the specified index
246
     *
247
     * This returns the element that was previously at this index
248
     *
249
     * @param int $index
250
     * @return mixed
251
     * @throws \OutOfBoundsException if the index doesn't exist
252
     */
253
    public function removeAt(int $index)
254
    {
255
        $oldElement = $this->get($index);
256
        array_splice($this->elements, $index, 1);
257 2
258
        return $oldElement;
259 2
    }
260 1
261
    /**
262 1
     * Replace the element at the specified index
263
     *
264
     * Returns the element that was previously at this index
265
     *
266
     * @param int $index
267
     * @param mixed $element
268
     * @return mixed
269
     * @throws \OutOfBoundsException if the index doesn't exist
270
     */
271
    public function set(int $index, $element)
272
    {
273
        $this->assertIndex($index);
274
275 3
        $oldElement = null;
276
        if ($this->has($index)) {
277 3
            $oldElement = $this->get($index);
278 1
        }
279
280
        $this->elements[$index] = $element;
281 2
282 2
        return $oldElement;
283 1
    }
284
285
    /**
286 2
     * Returns a new ListInterface ranging from $fromIndex inclusive to
287
     * $toIndex exclusive
288 2
     *
289
     * @param int $fromIndex
290
     * @param int $toIndex
291
     * @return ListInterface
292
     * @throws \OutOfBoundsException If to or from index does not exist
293
     */
294
    public function subList(int $fromIndex, int $toIndex): ListInterface
295
    {
296
        if (!$this->has($fromIndex) || !$this->has($toIndex - 1)) {
297
            throw new OutOfBoundsException('Unable to create sub list as $toIndex or $fromIndex do not exist in list');
298
        }
299
300 4
        $newList = array_slice($this->elements, $fromIndex, $toIndex - $fromIndex);
301
302 4
        return new ArrayList($newList);
303 2
    }
304
305
    /**
306 2
     * Assert the index is able to be inserted
307
     *
308 2
     * @param int $index
309
     * @return void
310
     * @throws \OutOfBoundsException If the index is less than 0 or greater than current size
311
     */
312
    private function assertIndex(int $index):void
313
    {
314
        if ($index < 0 || $index > $this->count()) {
315
            throw new OutOfBoundsException(sprintf('Element unable to be inserted at index "%s"', $index));
316
        }
317
    }
318
}
319