Completed
Push — master ( 5856c5...90f7ba )
by Rudi
05:12
created

Set::remove()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 1
crap 2
1
<?php
2
namespace Ds;
3
4
use Error;
5
use OutOfBoundsException;
6
use OutOfRangeException;
7
use Traversable;
8
9
/**
10
 * Set
11
 *
12
 * @package Ds
13
 */
14
final class Set implements \IteratorAggregate, \ArrayAccess, Collection
15
{
16
    use Traits\Collection;
17
18
    const MIN_CAPACITY = Map::MIN_CAPACITY;
19
20
    /**
21
     * @var Map
22
     */
23
    private $internal;
24
25
    /**
26
     * Creates a new set using the values of an array or Traversable object.
27
     * The keys of either will not be preserved.
28
     *
29
     * @param array|\Traversable|null $values
30
     */
31 509
    public function __construct($values = null)
32
    {
33 509
        $this->internal = new Map();
34
35 509
        if (func_num_args()) {
36 505
            $this->add(...$values);
37
        }
38 509
    }
39
40
    /**
41
     * Adds zero or more values to the set.
42
     *
43
     * @param mixed ...$values
44
     */
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $values a bit more specific; maybe use array.
Loading history...
45 507
    public function add(...$values)
46
    {
47 507
        foreach ($values as $value) {
48 350
            $this->internal->put($value, null);
49
        }
50 507
    }
51
52
    /**
53
     * Ensures that enough memory is allocated for a specified capacity. This
54
     * potentially reduces the number of reallocations as the size increases.
55
     *
56
     * @param int $capacity The number of values for which capacity should be
57
     *                      allocated. Capacity will stay the same if this value
58
     *                      is less than or equal to the current capacity.
59
     */
60 6
    public function allocate(int $capacity)
61
    {
62 6
        $this->internal->allocate($capacity);
63 6
    }
64
65
    /**
66
     * Returns the current capacity of the set.
67
     *
68
     * @return int
69
     */
70 9
    public function capacity(): int
71
    {
72 9
        return $this->internal->capacity();
73
    }
74
75
    /**
76
     * Clear all elements in the Set
77
     */
78 2
    public function clear()
79
    {
80 2
        $this->internal->clear();
81 2
    }
82
83
    /**
84
     * Determines whether the set contains all of zero or more values.
85
     *
86
     * @param mixed ...$values
87
     *
88
     * @return bool true if at least one value was provided and the set
89
     *              contains all given values, false otherwise.
90
     */
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $values a bit more specific; maybe use array.
Loading history...
91 12
    public function contains(...$values): bool
92
    {
93 12
        foreach ($values as $value) {
94 10
            if ( ! $this->internal->hasKey($value)) {
95 10
                return false;
96
            }
97
        }
98
99 7
        return true;
100
    }
101
102
    /**
103
     * @inheritDoc
104
     */
105 22
    public function copy(): \Ds\Collection
106
    {
107 22
        return new self($this);
108
    }
109
110
    /**
111
     * Returns the number of elements in the Stack
112
     *
113
     * @return int
114
     */
115 62
    public function count(): int
116
    {
117 62
        return count($this->internal);
118
    }
119
120
    /**
121
     * Creates a new set using values from this set that aren't in another set.
122
     *
123
     * Formally: A \ B = {x ∈ A | x ∉ B}
124
     *
125
     * @param Set $set
126
     *
127
     * @return Set
128
     */
129 12
    public function diff(Set $set): Set
130
    {
131 12
        return $this->internal->diff($set->internal)->keys();
132
    }
133
134
    /**
135
     * Creates a new set using values in either this set or in another set,
136
     * but not in both.
137
     *
138
     * Formally: A ⊖ B = {x : x ∈ (A \ B) ∪ (B \ A)}
139
     *
140
     * @param Set $set
141
     *
142
     * @return Set
143
     */
144 8
    public function xor(Set $set): Set
145
    {
146 8
        return $this->internal->xor($set->internal)->keys();
147
    }
148
149
    /**
150
     * Returns a new set containing only the values for which a callback
151
     * returns true. A boolean test will be used if a callback is not provided.
152
     *
153
     * @param callable|null $callback Accepts a value, returns a boolean:
154
     *                                 true : include the value,
155
     *                                 false: skip the value.
156
     *
157
     * @return Set
158
     */
159 8
    public function filter(callable $callback = null): Set
160
    {
161 8
        return new self(array_filter($this->toArray(), $callback ?: 'boolval'));
162
    }
163
164
    /**
165
     * Returns the first value in the set.
166
     *
167
     * @return mixed the first value in the set.
168
     */
169 4
    public function first()
170
    {
171 4
        return $this->internal->first()->key;
172
    }
173
174
    /**
175
     * Returns the value at a specified position in the set.
176
     *
177
     * @param int $position
178
     *
179
     * @return mixed|null
180
     *
181
     * @throws OutOfRangeException
182
     */
183 13
    public function get(int $position)
184
    {
185 13
        return $this->internal->skip($position)->key;
186
    }
187
188
    /**
189
     * Creates a new set using values common to both this set and another set.
190
     *
191
     * In other words, returns a copy of this set with all values removed that
192
     * aren't in the other set.
193
     *
194
     * Formally: A ∩ B = {x : x ∈ A ∧ x ∈ B}
195
     *
196
     * @param Set $set
197
     *
198
     * @return Set
199
     */
200 14
    public function intersect(Set $set): Set
201
    {
202 14
        return $this->internal->intersect($set->internal)->keys();
203
    }
204
205
    /**
206
     * @inheritDoc
207
     */
208 3
    public function isEmpty(): bool
209
    {
210 3
        return $this->internal->isEmpty();
211
    }
212
213
    /**
214
     * Joins all values of the set into a string, adding an optional 'glue'
215
     * between them. Returns an empty string if the set is empty.
216
     *
217
     * @param string $glue
0 ignored issues
show
Documentation introduced by
Should the type for parameter $glue not be null|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
218
     *
219
     * @return string
220
     */
221 48
    public function join(string $glue = null): string
222
    {
223 48
        return implode($glue, $this->toArray());
224
    }
225
226
    /**
227
     * Returns the last value in the set.
228
     *
229
     * @return mixed the last value in the set.
230
     */
231 4
    public function last()
232
    {
233 4
        return $this->internal->last()->key;
234
    }
235
236
    /**
237
     * Iteratively reduces the set to a single value using a callback.
238
     *
239
     * @param callable $callback Accepts the carry and current value, and
240
     *                           returns an updated carry value.
241
     *
242
     * @param mixed|null $initial Optional initial carry value.
243
     *
244
     * @return mixed The carry value of the final iteration, or the initial
245
     *               value if the set was empty.
246
     */
247 8
    public function reduce(callable $callback, $initial = null)
248
    {
249 8
        $carry = $initial;
250
251 8
        foreach ($this as $value) {
252 6
            $carry = $callback($carry, $value);
253
        }
254
255 6
        return $carry;
256
    }
257
258
    /**
259
     * Removes zero or more values from the set.
260
     *
261
     * @param mixed ...$values
262
     */
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $values a bit more specific; maybe use array.
Loading history...
263 14
    public function remove(...$values)
264
    {
265 14
        foreach ($values as $value) {
266 13
            $this->internal->remove($value, null);
267
        }
268 14
    }
269
270
    /**
271
     * Reverses the set in-place.
272
     */
273 5
    public function reverse()
274
    {
275 5
        $this->internal->reverse();
276 5
    }
277
278
    /**
279
     * Returns a reversed copy of the set.
280
     *
281
     * @return Set
282
     */
283 5
    public function reversed(): Set
284
    {
285 5
        $reversed = $this->copy();
286 5
        $reversed->internal->reverse();
0 ignored issues
show
Bug introduced by
Accessing internal on the interface Ds\Collection suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
287
288 5
        return $reversed;
289
    }
290
291
    /**
292
     * Returns a subset of a given length starting at a specified offset.
293
     *
294
     * @param int $offset If the offset is non-negative, the set will start
295
     *                    at that offset in the set. If offset is negative,
296
     *                    the set will start that far from the end.
297
     *
298
     * @param int $length If a length is given and is positive, the resulting
0 ignored issues
show
Documentation introduced by
Should the type for parameter $length not be null|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
299
     *                    set will have up to that many values in it.
300
     *                    If the requested length results in an overflow, only
301
     *                    values up to the end of the set will be included.
302
     *
303
     *                    If a length is given and is negative, the set
304
     *                    will stop that many values from the end.
305
     *
306
     *                    If a length is not provided, the resulting set
307
     *                    will contains all values between the offset and the
308
     *                    end of the set.
309
     *
310
     * @return Set
311
     */
312 201
    public function slice(int $offset, int $length = null): Set
313
    {
314 201
        $sliced = new self();
315 201
        $sliced->internal = $this->internal->slice($offset, $length);
316
317 201
        return $sliced;
318
    }
319
320
    /**
321
     * Sorts the set in-place, based on an optional callable comparator.
322
     *
323
     * @param callable|null $comparator Accepts two values to be compared.
324
     *                                  Should return the result of a <=> b.
325
     */
326 2
    public function sort(callable $comparator = null)
327
    {
328 2
        $this->internal->ksort($comparator);
329 2
    }
330
331
    /**
332
     * Returns a sorted copy of the set, based on an optional callable
333
     * comparator. Natural ordering will be used if a comparator is not given.
334
     *
335
     * @param callable|null $comparator Accepts two values to be compared.
336
     *                                  Should return the result of a <=> b.
337
     *
338
     * @return Set
339
     */
340 2
    public function sorted(callable $comparator = null): Set
341
    {
342 2
        $sorted = $this->copy();
343 2
        $sorted->internal->ksort($comparator);
0 ignored issues
show
Bug introduced by
Accessing internal on the interface Ds\Collection suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
344
345 2
        return $sorted;
346
    }
347
348
    /**
349
     * Returns the result of adding all given values to the set.
350
     *
351
     * @param array|\Traversable $values
352
     *
353
     * @return \Ds\Set
354
     */
355 10
    public function merge($values): Set
356
    {
357 10
        $merged = $this->copy();
358
359 10
        foreach ($values as $value) {
360 6
            $merged->add($value);
361
        }
362
363 10
        return $merged;
364
    }
365
366
    /**
367
     * @inheritDoc
368
     */
369 430
    public function toArray(): array
370
    {
371 430
        return iterator_to_array($this);
372
    }
373
374
    /**
375
     * Returns the sum of all values in the set.
376
     *
377
     * @return int|float The sum of all the values in the set.
378
     */
379 7
    public function sum()
380
    {
381 7
        return array_sum($this->toArray());
382
    }
383
384
    /**
385
     * Creates a new set that contains the values of this set as well as the
386
     * values of another set.
387
     *
388
     * Formally: A ∪ B = {x: x ∈ A ∨ x ∈ B}
389
     *
390
     * @param Set $set
391
     *
392
     * @return Set
393
     */
394 12
    public function union(Set $set): Set
395
    {
396 12
        $union = new self();
397
398 12
        foreach ($this as $value) {
399 8
            $union->add($value);
400
        }
401
402 12
        foreach ($set as $value) {
403 8
            $union->add($value);
404
        }
405
406 12
        return $union;
407
    }
408
409
    /**
410
     * Get iterator
411
     */
412 441
    public function getIterator()
413
    {
414 441
        foreach ($this->internal as $key => $value) {
415 297
            yield $key;
416
        }
417 441
    }
418
419
    /**
420
     * @inheritdoc
421
     *
422
     * @throws OutOfBoundsException
423
     */
424 14
    public function offsetSet($offset, $value)
425
    {
426 14
        if ($offset === null) {
427 13
            $this->add($value);
428 13
            return;
429
        }
430
431 1
        throw new OutOfBoundsException();
432
    }
433
434
    /**
435
     * @inheritdoc
436
     */
437 14
    public function offsetGet($offset)
438
    {
439 14
        return $this->internal->skip($offset)->key;
440
    }
441
442
    /**
443
     * @inheritdoc
444
     *
445
     * @throws Error
446
     */
447 2
    public function offsetExists($offset)
448
    {
449 2
        throw new Error();
450
    }
451
452
    /**
453
     * @inheritdoc
454
     *
455
     * @throws Error
456
     */
457 1
    public function offsetUnset($offset)
458
    {
459 1
        throw new Error();
460
    }
461
}
462