Completed
Push — master ( 50814b...1592db )
by Rudi
02:18
created

Set::addAll()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 3
nop 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A Set::allocate() 0 4 1
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
     *
30
     * Should an integer be provided the Set will allocate the memory capacity
31
     * to the size of $values.
32
     *
33
     * @param array|\Traversable|int|null $values
34
     */
35
    public function __construct($values = null)
36
    {
37
        $this->internal = new Map();
38
39
        if ($values && is_array($values) || $values instanceof Traversable) {
40
            $this->add(...$values);
41
        }
42
    }
43
44
    /**
45
     * Adds zero or more values to the set.
46
     *
47
     * @param mixed ...$values
48
     */
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $values a bit more specific; maybe use array.
Loading history...
49
    public function add(...$values)
50
    {
51
        foreach ($values as $value) {
52
            $this->internal[$value] = null;
53
        }
54
    }
55
56
    /**
57
     * Ensures that enough memory is allocated for a specified capacity. This
58
     * potentially reduces the number of reallocations as the size increases.
59
     *
60
     * @param int $capacity The number of values for which capacity should be
61
     *                      allocated. Capacity will stay the same if this value
62
     *                      is less than or equal to the current capacity.
63
     */
64
    public function allocate(int $capacity)
65
    {
66
        $this->internal->allocate($capacity);
67
    }
68
69
    /**
70
     * Returns the current capacity of the set.
71
     *
72
     * @return int
73
     */
74
    public function capacity(): int
75
    {
76
        return $this->internal->capacity();
77
    }
78
79
    /**
80
     * Clear all elements in the Set
81
     */
82
    public function clear()
83
    {
84
        $this->internal->clear();
85
    }
86
87
    /**
88
     * Determines whether the set contains all of zero or more values.
89
     *
90
     * @param mixed ...$values
91
     *
92
     * @return bool true if at least one value was provided and the set
93
     *              contains all given values, false otherwise.
94
     */
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $values a bit more specific; maybe use array.
Loading history...
95
    public function contains(...$values): bool
96
    {
97
        if ( ! $values) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $values of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
98
            return false;
99
        }
100
101
        foreach ($values as $value) {
102
            if ( ! $this->internal->hasKey($value)) {
103
                return false;
104
            }
105
        }
106
107
        return true;
108
    }
109
110
    /**
111
     * @inheritDoc
112
     */
113
    public function copy()
114
    {
115
        return new Set($this);
116
    }
117
118
    /**
119
     * Returns the number of elements in the Stack
120
     *
121
     * @return int
122
     */
123
    public function count(): int
124
    {
125
        return count($this->internal);
126
    }
127
128
    /**
129
     * Creates a new set using values from this set that aren't in another set.
130
     *
131
     * Formally: A \ B = {x ∈ A | x ∉ B}
132
     *
133
     * @param Set $set
134
     *
135
     * @return Set
136
     */
137
    public function diff(Set $set): Set
138
    {
139
        $diff = new Set();
140
141
        foreach ($this as $value) {
142
            if ($set->contains($value)) {
143
                continue;
144
            }
145
146
            $diff->add($value);
147
        }
148
149
        return $diff;
150
    }
151
152
    /**
153
     * Creates a new set using values in either this set or in another set,
154
     * but not in both.
155
     *
156
     * Formally: A ⊖ B = {x : x ∈ (A \ B) ∪ (B \ A)}
157
     *
158
     * @param Set $set
159
     *
160
     * @return Set
161
     */
162
    public function xor(Set $set): Set
163
    {
164
        return $this->internal->xor($set->internal)->keys();
165
    }
166
167
    /**
168
     * Returns a new set containing only the values for which a predicate
169
     * returns true. A boolean test will be used if a predicate is not provided.
170
     *
171
     * @param callable|null $predicate Accepts a value, returns a boolean:
172
     *                                 true : include the value,
173
     *                                 false: skip the value.
174
     *
175
     * @return Set
176
     */
177 View Code Duplication
    public function filter(callable $predicate = null): Set
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178
    {
179
        $filtered = new Set();
180
181
        foreach ($this as $value) {
182
            if ($predicate ? $predicate($value) : $value) {
183
                $filtered->add($value);
184
            }
185
        }
186
187
        return $filtered;
188
    }
189
190
    /**
191
     * Returns the first value in the set.
192
     *
193
     * @return mixed the first value in the set.
194
     */
195
    public function first()
196
    {
197
        return $this->internal->first()->key;
198
    }
199
200
    /**
201
     * Returns the value at a specified position in the set.
202
     *
203
     * @param int $position
204
     *
205
     * @return mixed|null
206
     *
207
     * @throws OutOfRangeException
208
     */
209
    public function get(int $position)
210
    {
211
        return $this->internal->skip($position)->key;
212
    }
213
214
    /**
215
     * Creates a new set using values common to both this set and another set.
216
     * In other words, returns a copy of this set with all values removed that
217
     * aren't in the other set.
218
     *
219
     * Formally: A ∩ B = {x : x ∈ A ∧ x ∈ B}
220
     *
221
     * @param Set $set
222
     *
223
     * @return Set
224
     */
225
    public function intersect(Set $set): Set
226
    {
227
        return $this->internal->intersect($set->internal)->keys();
228
    }
229
230
    /**
231
     * @inheritDoc
232
     */
233
    public function isEmpty(): bool
234
    {
235
        return $this->internal->isEmpty();
236
    }
237
238
    /**
239
     * Joins all values of the set into a string, adding an optional 'glue'
240
     * between them. Returns an empty string if the set is empty.
241
     *
242
     * @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...
243
     *
244
     * @return string
245
     */
246
    public function join(string $glue = null): string
247
    {
248
        return implode($glue, $this->toArray());
249
    }
250
251
    /**
252
     * Returns the last value in the set.
253
     *
254
     * @return mixed the last value in the set.
255
     */
256
    public function last()
257
    {
258
        return $this->internal->last()->key;
259
    }
260
261
    /**
262
     * Iteratively reduces the set to a single value using a callback.
263
     *
264
     * @param callable $callback Accepts the carry and current value, and
265
     *                           returns an updated carry value.
266
     *
267
     * @param mixed|null $initial Optional initial carry value.
268
     *
269
     * @return mixed The carry value of the final iteration, or the initial
270
     *               value if the set was empty.
271
     */
272
    public function reduce(callable $callback, $initial = null)
273
    {
274
        $carry = $initial;
275
276
        foreach ($this as $value) {
277
            $carry = $callback($carry, $value);
278
        }
279
280
        return $carry;
281
    }
282
283
    /**
284
     * Removes zero or more values from the set.
285
     *
286
     * @param mixed ...$values
287
     */
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $values a bit more specific; maybe use array.
Loading history...
288
    public function remove(...$values)
289
    {
290
        foreach ($values as $value) {
291
            $this->internal->remove($value, null);
292
        }
293
    }
294
295
    /**
296
     * Returns a reversed copy of the set.
297
     *
298
     * @return Set
299
     */
300
    public function reverse(): Set
301
    {
302
        $reversed = new Set();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 11 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
303
        $reversed->internal = $this->internal->reverse();
304
305
        return $reversed;
306
    }
307
308
    /**
309
     * Returns a subset of a given length starting at a specified offset.
310
     *
311
     * @param int $offset If the offset is non-negative, the set will start
312
     *                    at that offset in the set. If offset is negative,
313
     *                    the set will start that far from the end.
314
     *
315
     * @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...
316
     *                    set will have up to that many values in it.
317
     *                    If the requested length results in an overflow, only
318
     *                    values up to the end of the set will be included.
319
     *
320
     *                    If a length is given and is negative, the set
321
     *                    will stop that many values from the end.
322
     *
323
     *                    If a length is not provided, the resulting set
324
     *                    will contains all values between the offset and the
325
     *                    end of the set.
326
     *
327
     * @return Set
328
     */
329
    public function slice(int $offset, int $length = null): Set
330
    {
331
        $sliced = new Set();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 11 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
332
        $sliced->internal = $this->internal->slice($offset, $length);
333
334
        return $sliced;
335
    }
336
337
    /**
338
     * Returns a sorted copy of the set, based on an optional callable
339
     * comparator. Natural ordering will be used if a comparator is not given.
340
     *
341
     * @param callable|null $comparator Accepts two values to be compared.
342
     *                                  Should return the result of a <=> b.
343
     *
344
     * @return Set
345
     */
346
    public function sort(callable $comparator = null): Set
347
    {
348
        $set = new Set();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 11 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
349
        $set->internal = $this->internal->ksort($comparator);
350
351
        return $set;
352
    }
353
354
    /**
355
     * @inheritDoc
356
     */
357
    public function toArray(): array
358
    {
359
        return iterator_to_array($this);
360
    }
361
362
    /**
363
     * Creates a new set that contains the values of this set as well as the
364
     * values of another set.
365
     *
366
     * Formally: A ∪ B = {x: x ∈ A ∨ x ∈ B}
367
     *
368
     * @param Set $set
369
     *
370
     * @return Set
371
     */
372
    public function union(Set $set): Set
373
    {
374
        $union = new Set();
375
376
        foreach ($this as $value) {
377
            $union->add($value);
378
        }
379
380
        foreach ($set as $value) {
381
            $union->add($value);
382
        }
383
384
        return $union;
385
    }
386
387
    /**
388
     * Get iterator
389
     */
390
    public function getIterator()
391
    {
392
        foreach ($this->internal as $key => $value) {
393
            yield $key;
394
        }
395
    }
396
397
    /**
398
     * @inheritdoc
399
     *
400
     * @throws OutOfBoundsException
401
     */
402
    public function offsetSet($offset, $value)
403
    {
404
        if ($offset === null) {
405
            $this->add($value);
406
            return;
407
        }
408
409
        throw new OutOfBoundsException();
410
    }
411
412
    /**
413
     * @inheritdoc
414
     */
415
    public function offsetGet($offset)
416
    {
417
        return $this->internal->skip($offset)->key;
418
    }
419
420
    /**
421
     * @inheritdoc
422
     *
423
     * @throws Error
424
     */
425
    public function offsetExists($offset)
426
    {
427
        throw new Error();
428
    }
429
430
    /**
431
     * @inheritdoc
432
     *
433
     * @throws Error
434
     */
435
    public function offsetUnset($offset)
436
    {
437
        throw new Error();
438
    }
439
}
440